mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-31 01:35:15 +02:00
Merge branch 'master' into OS141create-Log-Architectur
Conflicts: server/controllers/staff/delete.php tests/system/edit-settings.rb
This commit is contained in:
commit
e317bbea38
@ -19,13 +19,13 @@ class ArticleAddModal extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="add-article-modal">
|
<div className="article-add-modal">
|
||||||
<Header title={i18n('ADD_ARTICLE')} description={i18n('ADD_ARTICLE_DESCRIPTION', {category: this.props.topicName})} />
|
<Header title={i18n('ADD_ARTICLE')} description={i18n('ADD_ARTICLE_DESCRIPTION', {category: this.props.topicName})} />
|
||||||
<Form onSubmit={this.onAddNewArticleFormSubmit.bind(this)}>
|
<Form onSubmit={this.onAddNewArticleFormSubmit.bind(this)}>
|
||||||
<FormField name="title" label={i18n('TITLE')} field="input" fieldProps={{size: 'large'}} validation="TITLE" required/>
|
<FormField name="title" label={i18n('TITLE')} field="input" fieldProps={{size: 'large'}} validation="TITLE" required/>
|
||||||
<FormField name="content" label={i18n('CONTENT')} field="textarea" validation="TEXT_AREA" required/>
|
<FormField name="content" label={i18n('CONTENT')} field="textarea" validation="TEXT_AREA" required/>
|
||||||
<SubmitButton type="secondary">{i18n('ADD_ARTICLE')}</SubmitButton>
|
<SubmitButton type="secondary">{i18n('ADD_ARTICLE')}</SubmitButton>
|
||||||
<Button className="add-article-modal__cancel-button" type="link" onClick={(event) => {
|
<Button className="article-add-modal__cancel-button" type="link" onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
ModalContainer.closeModal();
|
ModalContainer.closeModal();
|
||||||
}}>{i18n('CANCEL')}</Button>
|
}}>{i18n('CANCEL')}</Button>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
.article-add-article {
|
.article-add-modal {
|
||||||
|
width: 800px;
|
||||||
|
|
||||||
&__cancel-button {
|
&__cancel-button {
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -11,6 +11,7 @@ import TopicEditModal from 'app-components/topic-edit-modal';
|
|||||||
import Loading from 'core-components/loading';
|
import Loading from 'core-components/loading';
|
||||||
import Button from 'core-components/button';
|
import Button from 'core-components/button';
|
||||||
import Icon from 'core-components/icon';
|
import Icon from 'core-components/icon';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class ArticlesList extends React.Component {
|
class ArticlesList extends React.Component {
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ class ArticlesList extends React.Component {
|
|||||||
editable: React.PropTypes.bool,
|
editable: React.PropTypes.bool,
|
||||||
articlePath: React.PropTypes.string,
|
articlePath: React.PropTypes.string,
|
||||||
loading: React.PropTypes.bool,
|
loading: React.PropTypes.bool,
|
||||||
|
errored: React.PropTypes.bool,
|
||||||
topics: React.PropTypes.array,
|
topics: React.PropTypes.array,
|
||||||
retrieveOnMount: React.PropTypes.bool
|
retrieveOnMount: React.PropTypes.bool
|
||||||
};
|
};
|
||||||
@ -34,6 +36,10 @@ class ArticlesList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if(this.props.errored) {
|
||||||
|
return <Message type="error">{i18n('ERROR_RETRIEVING_ARTICLES')}</Message>;
|
||||||
|
}
|
||||||
|
|
||||||
return (this.props.loading) ? <Loading /> : this.renderContent();
|
return (this.props.loading) ? <Loading /> : this.renderContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +90,7 @@ class ArticlesList extends React.Component {
|
|||||||
export default connect((store) => {
|
export default connect((store) => {
|
||||||
return {
|
return {
|
||||||
topics: store.articles.topics,
|
topics: store.articles.topics,
|
||||||
|
errored: store.articles.errored,
|
||||||
loading: store.articles.loading
|
loading: store.articles.loading
|
||||||
};
|
};
|
||||||
})(ArticlesList);
|
})(ArticlesList);
|
||||||
|
@ -10,7 +10,7 @@ class PeopleList extends React.Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
list: React.PropTypes.arrayOf(React.PropTypes.shape({
|
list: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||||
profilePic: React.PropTypes.string,
|
profilePic: React.PropTypes.string,
|
||||||
name: React.PropTypes.string,
|
name: React.PropTypes.node,
|
||||||
assignedTickets: React.PropTypes.number,
|
assignedTickets: React.PropTypes.number,
|
||||||
closedTickets: React.PropTypes.number,
|
closedTickets: React.PropTypes.number,
|
||||||
lastLogin: React.PropTypes.number
|
lastLogin: React.PropTypes.number
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
&__comment {
|
&__comment {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
&-pointer {
|
&-pointer {
|
||||||
right: 100%;
|
right: 100%;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
class TicketInfo extends React.Component {
|
class TicketInfo extends React.Component {
|
||||||
@ -16,12 +18,12 @@ class TicketInfo extends React.Component {
|
|||||||
{this.props.ticket.content}
|
{this.props.ticket.content}
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-info__author">
|
<div className="ticket-info__author">
|
||||||
Author: {this.props.ticket.author.name}
|
{i18n('AUTHOR')}: {this.props.ticket.author.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-info__properties">
|
<div className="ticket-info__properties">
|
||||||
<div className="ticket-info__properties__status">
|
<div className="ticket-info__properties__status">
|
||||||
<span className="ticket-info__properties__label">
|
<span className="ticket-info__properties__label">
|
||||||
Status:
|
{i18n('STATUS')}:
|
||||||
</span>
|
</span>
|
||||||
<span className={this.getStatusClass()}>
|
<span className={this.getStatusClass()}>
|
||||||
{(this.props.ticket.closed) ? 'closed' : 'open'}
|
{(this.props.ticket.closed) ? 'closed' : 'open'}
|
||||||
@ -29,7 +31,7 @@ class TicketInfo extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="ticket-info__properties__priority">
|
<div className="ticket-info__properties__priority">
|
||||||
<span className="ticket-info__properties__label">
|
<span className="ticket-info__properties__label">
|
||||||
Priority:
|
{i18n('PRIORITY')}:
|
||||||
</span>
|
</span>
|
||||||
<span className={this.getPriorityClass()}>
|
<span className={this.getPriorityClass()}>
|
||||||
{this.props.ticket.priority}
|
{this.props.ticket.priority}
|
||||||
@ -37,24 +39,25 @@ class TicketInfo extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="ticket-info__properties__owner">
|
<div className="ticket-info__properties__owner">
|
||||||
<span className="ticket-info__properties__label">
|
<span className="ticket-info__properties__label">
|
||||||
Owned:
|
{i18n('OWNED')}:
|
||||||
</span>
|
</span>
|
||||||
<span className="ticket-info__properties__badge-red">
|
<span className={this.getOwnedClass()}>
|
||||||
none
|
{(this.props.ticket.owner) ? i18n('YES') : i18n('NO')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-info__properties__comments">
|
<div className="ticket-info__properties__comments">
|
||||||
<span className="ticket-info__properties__label">
|
<span className="ticket-info__properties__label">
|
||||||
Comments:
|
{i18n('COMMENTS')}:
|
||||||
</span>
|
</span>
|
||||||
<span className="ticket-info__properties__badge-blue">
|
<span className="ticket-info__properties__badge-blue">
|
||||||
21
|
{_.filter(this.props.ticket.events, event => event.type === 'COMMENT').length}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatusClass() {
|
getStatusClass() {
|
||||||
if(this.props.ticket.closed) {
|
if(this.props.ticket.closed) {
|
||||||
return 'ticket-info__properties__badge-red';
|
return 'ticket-info__properties__badge-red';
|
||||||
@ -63,6 +66,14 @@ class TicketInfo extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOwnedClass() {
|
||||||
|
if(this.props.ticket.owner) {
|
||||||
|
return 'ticket-info__properties__badge-green';
|
||||||
|
} else {
|
||||||
|
return 'ticket-info__properties__badge-red';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getPriorityClass() {
|
getPriorityClass() {
|
||||||
let priorityClasses = {
|
let priorityClasses = {
|
||||||
'low': 'ticket-info__properties__badge-green',
|
'low': 'ticket-info__properties__badge-green',
|
||||||
|
@ -3,19 +3,28 @@
|
|||||||
.ticket-info {
|
.ticket-info {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
&__title{
|
|
||||||
|
&__title {
|
||||||
color: $primary-black;
|
color: $primary-black;
|
||||||
font-variant: small-caps;
|
font-size: $font-size--md;
|
||||||
}
|
}
|
||||||
&__description{
|
|
||||||
color: $grey;
|
&__description {
|
||||||
|
margin-top: 5px;
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
&__author{
|
|
||||||
|
&__author {
|
||||||
color: $primary-blue;
|
color: $primary-blue;
|
||||||
margin-bottom: 12px;
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
&__properties{
|
|
||||||
|
&__properties {
|
||||||
background-color: $grey;
|
background-color: $grey;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-variant: small-caps;
|
font-variant: small-caps;
|
||||||
@ -35,7 +44,7 @@
|
|||||||
|
|
||||||
&__badge-green,
|
&__badge-green,
|
||||||
&__badge-blue,
|
&__badge-blue,
|
||||||
&__badge-red{
|
&__badge-red {
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -44,19 +53,19 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__badge-green{
|
&__badge-green {
|
||||||
background-color: $primary-green;
|
background-color: $primary-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__badge-blue{
|
&__badge-blue {
|
||||||
background-color: $secondary-blue;
|
background-color: $secondary-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__badge-red{
|
&__badge-red {
|
||||||
background-color: $primary-red;
|
background-color: $primary-red;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__label{
|
&__label {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: 71px;
|
width: 71px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -64,10 +73,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__status,
|
&__status,
|
||||||
&__owner{
|
&__owner {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
width: 125px;
|
width: 125px;
|
||||||
.ticket-info__properties__label{
|
|
||||||
|
.ticket-info__properties__label {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ 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';
|
||||||
|
|
||||||
class TicketViewer extends React.Component {
|
class TicketViewer extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -188,6 +189,7 @@ class TicketViewer extends React.Component {
|
|||||||
<SubmitButton>{i18n('RESPOND_TICKET')}</SubmitButton>
|
<SubmitButton>{i18n('RESPOND_TICKET')}</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
{(this.state.commentError) ? this.renderCommentError() : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -216,6 +218,12 @@ class TicketViewer extends React.Component {
|
|||||||
return customResponsesNode;
|
return customResponsesNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderCommentError() {
|
||||||
|
return (
|
||||||
|
<Message className="ticket-viewer__message" type="error">{i18n('TICKET_COMMENT_ERROR')}</Message>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getCommentFormProps() {
|
getCommentFormProps() {
|
||||||
return {
|
return {
|
||||||
onSubmit: this.onSubmit.bind(this),
|
onSubmit: this.onSubmit.bind(this),
|
||||||
@ -316,7 +324,8 @@ class TicketViewer extends React.Component {
|
|||||||
|
|
||||||
onCommentSuccess() {
|
onCommentSuccess() {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false
|
loading: false,
|
||||||
|
commentError: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.onTicketModification();
|
this.onTicketModification();
|
||||||
@ -324,7 +333,8 @@ class TicketViewer extends React.Component {
|
|||||||
|
|
||||||
onCommentFail() {
|
onCommentFail() {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false
|
loading: false,
|
||||||
|
commentError: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {Link} from 'react-router';
|
import {Link} from 'react-router';
|
||||||
@ -78,11 +79,33 @@ class TopicViewer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderArticleItem(article, index) {
|
renderArticleItem(article, index) {
|
||||||
|
let props = {
|
||||||
|
className: 'topic-viewer__list-item',
|
||||||
|
key: index,
|
||||||
|
draggable: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if(this.props.editable) {
|
||||||
|
_.extend(props, {
|
||||||
|
onDragOver: (this.state.currentDraggedId) ? this.onItemDragOver.bind(this, article, index) : null,
|
||||||
|
onDrop: (this.state.currentDraggedId) ? this.onItemDrop.bind(this, article, index) : null,
|
||||||
|
onDragStart: () => {
|
||||||
|
this.setState({currentDraggedId: article.id})
|
||||||
|
},
|
||||||
|
onDragEnd: () => {
|
||||||
|
if(this.state.currentDraggedId) {
|
||||||
|
this.setState({articles: this.props.articles, currentDraggedId: 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="topic-viewer__list-item" key={index}>
|
<li {...props}>
|
||||||
<Link {...this.getArticleLinkProps(article, index)}>
|
<Link {...this.getArticleLinkProps(article, index)}>
|
||||||
{article.title}
|
{article.title}
|
||||||
</Link>
|
</Link>
|
||||||
|
<Icon className="topic-viewer__grab-icon" name="arrows" ref={'grab-' + index}/>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -126,31 +149,16 @@ class TopicViewer extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getArticleLinkProps(article, index) {
|
getArticleLinkProps(article) {
|
||||||
let classes = {
|
let classes = {
|
||||||
'topic-viewer__list-item-button': true,
|
'topic-viewer__list-item-button': true,
|
||||||
'topic-viewer__list-item-hidden': article.hidden
|
'topic-viewer__list-item-hidden': article.hidden
|
||||||
};
|
};
|
||||||
|
|
||||||
let props = {
|
return {
|
||||||
className: classNames(classes),
|
className: classNames(classes),
|
||||||
to: this.props.articlePath + article.id
|
to: this.props.articlePath + article.id
|
||||||
};
|
};
|
||||||
|
|
||||||
if(this.props.editable) {
|
|
||||||
_.extend(props, {
|
|
||||||
onDragOver: this.onItemDragOver.bind(this, article, index),
|
|
||||||
onDrop: this.onItemDrop.bind(this, article, index),
|
|
||||||
onDragStart: () => this.setState({currentDraggedId: article.id}),
|
|
||||||
onDragEnd: () => {
|
|
||||||
if(this.state.currentDraggedId) {
|
|
||||||
this.setState({articles: this.props.articles});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return props;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteClick() {
|
onDeleteClick() {
|
||||||
|
@ -38,12 +38,22 @@
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
color: $secondary-blue;
|
color: $secondary-blue;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&-hidden {
|
&-hidden {
|
||||||
width: 80%;
|
|
||||||
display: inline-block;
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.topic-viewer__grab-icon {
|
||||||
|
display: inline-block;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-item:before {
|
&-item:before {
|
||||||
@ -52,10 +62,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-item-button {
|
&-item-button {
|
||||||
|
display: inline-block;
|
||||||
color: $secondary-blue;
|
color: $secondary-blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__grab-icon {
|
||||||
|
color: $grey;
|
||||||
|
cursor: move;
|
||||||
|
margin-left: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&__add-item {
|
&__add-item {
|
||||||
color: $dark-grey;
|
color: $dark-grey;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
import RichTextEditor from 'react-rte-browserify';
|
import RichTextEditor from 'react-rte-browserify';
|
||||||
|
|
||||||
import ArticlesActions from 'actions/articles-actions';
|
import ArticlesActions from 'actions/articles-actions';
|
||||||
@ -9,6 +10,7 @@ import i18n from 'lib-app/i18n';
|
|||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
import DateTransformer from 'lib-core/date-transformer';
|
import DateTransformer from 'lib-core/date-transformer';
|
||||||
|
|
||||||
|
import AreYouSure from 'app-components/are-you-sure';
|
||||||
import Header from 'core-components/header';
|
import Header from 'core-components/header';
|
||||||
import Loading from 'core-components/loading';
|
import Loading from 'core-components/loading';
|
||||||
import Button from 'core-components/button';
|
import Button from 'core-components/button';
|
||||||
@ -61,10 +63,14 @@ class AdminPanelViewArticle extends React.Component {
|
|||||||
renderArticlePreview(article) {
|
renderArticlePreview(article) {
|
||||||
return (
|
return (
|
||||||
<div className="admin-panel-view-article__content">
|
<div className="admin-panel-view-article__content">
|
||||||
<div className="admin-panel-view-article__edit-button">
|
<div className="admin-panel-view-article__edit-buttons">
|
||||||
<Button size="medium" onClick={this.onEditClick.bind(this, article)}>{i18n('EDIT')}</Button>
|
<Button className="admin-panel-view-article__edit-button" size="medium" onClick={this.onEditClick.bind(this, article)} type="tertiary">
|
||||||
|
{i18n('EDIT')}
|
||||||
|
</Button>
|
||||||
|
<Button size="medium" onClick={this.onDeleteClick.bind(this, article)}>
|
||||||
|
{i18n('DELETE')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="admin-panel-view-article__article">
|
<div className="admin-panel-view-article__article">
|
||||||
<Header title={article.title}/>
|
<Header title={article.title}/>
|
||||||
|
|
||||||
@ -116,6 +122,10 @@ class AdminPanelViewArticle extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDeleteClick(article) {
|
||||||
|
AreYouSure.openModal(i18n('DELETE_ARTICLE_DESCRIPTION'), this.onArticleDeleted.bind(this, article));
|
||||||
|
}
|
||||||
|
|
||||||
onFormSubmit(form) {
|
onFormSubmit(form) {
|
||||||
API.call({
|
API.call({
|
||||||
path: '/article/edit',
|
path: '/article/edit',
|
||||||
@ -139,6 +149,15 @@ class AdminPanelViewArticle extends React.Component {
|
|||||||
editable: false
|
editable: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onArticleDeleted(article) {
|
||||||
|
API.call({
|
||||||
|
path: '/article/delete',
|
||||||
|
data: {
|
||||||
|
articleId: article.id
|
||||||
|
}
|
||||||
|
}).then(() => browserHistory.push('/admin/panel/articles/list-articles'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect((store) => {
|
export default connect((store) => {
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
.admin-panel-view-article {
|
.admin-panel-view-article {
|
||||||
|
|
||||||
&__edit-button {
|
&__edit-buttons {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__edit-button {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
&__last-edited {
|
&__last-edited {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {connect} from 'react-redux';
|
import {browserHistory} from 'react-router';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
@ -36,7 +36,8 @@ class AdminPanelViewStaff extends React.Component {
|
|||||||
|
|
||||||
getProps() {
|
getProps() {
|
||||||
return _.extend({}, this.state.userData, {
|
return _.extend({}, this.state.userData, {
|
||||||
staffId: this.props.params.staffId * 1
|
staffId: this.props.params.staffId * 1,
|
||||||
|
onDelete: this.onDelete.bind(this)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +47,10 @@ class AdminPanelViewStaff extends React.Component {
|
|||||||
userData: result.data
|
userData: result.data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDelete() {
|
||||||
|
browserHistory.push('/admin/panel/staff/staff-members');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminPanelViewStaff;
|
export default AdminPanelViewStaff;
|
@ -5,10 +5,13 @@ 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 TicketList from 'app-components/ticket-list';
|
import TicketList from 'app-components/ticket-list';
|
||||||
|
import AreYouSure from 'app-components/are-you-sure';
|
||||||
|
|
||||||
import Form from 'core-components/form';
|
import Form from 'core-components/form';
|
||||||
import FormField from 'core-components/form-field';
|
import FormField from 'core-components/form-field';
|
||||||
import SubmitButton from 'core-components/submit-button';
|
import SubmitButton from 'core-components/submit-button';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
|
||||||
class StaffEditor extends React.Component {
|
class StaffEditor extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -20,18 +23,21 @@ class StaffEditor extends React.Component {
|
|||||||
level: React.PropTypes.number.isRequired,
|
level: React.PropTypes.number.isRequired,
|
||||||
tickets: React.PropTypes.array.isRequired,
|
tickets: React.PropTypes.array.isRequired,
|
||||||
departments: React.PropTypes.array.isRequired,
|
departments: React.PropTypes.array.isRequired,
|
||||||
onChange: React.PropTypes.func
|
onChange: React.PropTypes.func,
|
||||||
|
onDelete: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
email: this.props.email,
|
email: this.props.email,
|
||||||
level: this.props.level - 1,
|
level: this.props.level - 1,
|
||||||
|
message: null,
|
||||||
departments: this.getUserDepartments()
|
departments: this.getUserDepartments()
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="staff-editor">
|
<div className="staff-editor">
|
||||||
|
{(this.state.message) ? this.renderMessage() : null}
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
<div className="staff-editor__card">
|
<div className="staff-editor__card">
|
||||||
@ -67,12 +73,12 @@ class StaffEditor extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
<div className="staff-editor__form">
|
<div className="staff-editor__form">
|
||||||
<Form className="staff-editor__update-email" values={{email: this.state.email}} onChange={form => this.setState({email: form.email})} onSubmit={this.onSubmit.bind(this)}>
|
<Form className="staff-editor__update-email" values={{email: this.state.email}} onChange={form => this.setState({email: form.email})} onSubmit={this.onSubmit.bind(this, 'EMAIL')}>
|
||||||
<FormField name="email" validation="EMAIL" required label={i18n('EMAIL')} fieldProps={{size: 'large'}}/>
|
<FormField name="email" validation="EMAIL" required label={i18n('EMAIL')} fieldProps={{size: 'large'}}/>
|
||||||
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE_EMAIL')}</SubmitButton>
|
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE_EMAIL')}</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
<span className="separator staff-editor__separator" />
|
<span className="separator staff-editor__separator" />
|
||||||
<Form className="staff-editor__update-password" onSubmit={this.onSubmit.bind(this)}>
|
<Form className="staff-editor__update-password" onSubmit={this.onSubmit.bind(this, 'PASSWORD')}>
|
||||||
<FormField name="password" validation="PASSWORD" required label={i18n('PASSWORD')} fieldProps={{size: 'large', password: true}}/>
|
<FormField name="password" validation="PASSWORD" required label={i18n('PASSWORD')} fieldProps={{size: 'large', password: true}}/>
|
||||||
<FormField name="rpassword" validation="REPEAT_PASSWORD" required label={i18n('REPEAT_PASSWORD')} fieldProps={{size: 'large', password: true}}/>
|
<FormField name="rpassword" validation="REPEAT_PASSWORD" required label={i18n('REPEAT_PASSWORD')} fieldProps={{size: 'large', password: true}}/>
|
||||||
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE_PASSWORD')}</SubmitButton>
|
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE_PASSWORD')}</SubmitButton>
|
||||||
@ -96,15 +102,41 @@ class StaffEditor extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(this.props.tickets) ? this.renderTickets() : null}
|
{(this.props.tickets) ? this.renderTickets() : null}
|
||||||
|
{(!this.props.myAccount) ? this.renderDelete() : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderMessage() {
|
||||||
|
let messageType = (this.state.message === 'FAIL') ? 'error' : 'success';
|
||||||
|
let message = null;
|
||||||
|
|
||||||
|
switch (this.state.message) {
|
||||||
|
case 'EMAIL':
|
||||||
|
message = 'EMAIL_CHANGED';
|
||||||
|
break;
|
||||||
|
case 'PASSWORD':
|
||||||
|
message = 'PASSWORD_CHANGED';
|
||||||
|
break;
|
||||||
|
case 'LEVEL':
|
||||||
|
message = 'LEVEL_UPDATED';
|
||||||
|
break;
|
||||||
|
case 'DEPARTMENTS':
|
||||||
|
message = 'DEPARTMENTS_UPDATED';
|
||||||
|
break;
|
||||||
|
case 'FAIL':
|
||||||
|
message = 'FAILED_EDIT_STAFF';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Message className="staff-editor__message" type={messageType}>{i18n(message)}</Message>;
|
||||||
|
}
|
||||||
|
|
||||||
renderLevelForm() {
|
renderLevelForm() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<span className="separator staff-editor__separator"/>
|
<span className="separator staff-editor__separator"/>
|
||||||
<Form className="staff-editor__update-level" values={{level: this.state.level}} onChange={form => this.setState({level: form.level})} onSubmit={this.onSubmit.bind(this)}>
|
<Form className="staff-editor__update-level" values={{level: this.state.level}} onChange={form => this.setState({level: form.level})} onSubmit={this.onSubmit.bind(this, 'LEVEL')}>
|
||||||
<FormField name="level" label={i18n('LEVEL')} field="select" fieldProps={{
|
<FormField name="level" label={i18n('LEVEL')} field="select" fieldProps={{
|
||||||
items: [{content: i18n('LEVEL_1')}, {content: i18n('LEVEL_2')}, {content: i18n('LEVEL_3')}],
|
items: [{content: i18n('LEVEL_1')}, {content: i18n('LEVEL_2')}, {content: i18n('LEVEL_3')}],
|
||||||
size: 'large'
|
size: 'large'
|
||||||
@ -117,7 +149,7 @@ class StaffEditor extends React.Component {
|
|||||||
|
|
||||||
renderDepartmentsForm() {
|
renderDepartmentsForm() {
|
||||||
return (
|
return (
|
||||||
<Form values={{departments: this.state.departments}} onChange={form => this.setState({departments: form.departments})} onSubmit={this.onSubmit.bind(this)}>
|
<Form values={{departments: this.state.departments}} onChange={form => this.setState({departments: form.departments})} onSubmit={this.onSubmit.bind(this, 'DEPARTMENTS')}>
|
||||||
<FormField name="departments" field="checkbox-group" fieldProps={{items: this.getDepartments()}} />
|
<FormField name="departments" field="checkbox-group" fieldProps={{items: this.getDepartments()}} />
|
||||||
<SubmitButton size="medium">{i18n('UPDATE_DEPARTMENTS')}</SubmitButton>
|
<SubmitButton size="medium">{i18n('UPDATE_DEPARTMENTS')}</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
@ -144,6 +176,22 @@ class StaffEditor extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderDelete() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span className="separator"/>
|
||||||
|
<div className="staff-editor__delete">
|
||||||
|
<div className="staff-editor__delete-title">
|
||||||
|
{i18n('DELETE_STAFF_MEMBER')}
|
||||||
|
</div>
|
||||||
|
<Button onClick={AreYouSure.openModal.bind(this, i18n('WILL_DELETE_STAFF'), this.onDeleteClick.bind(this))}>
|
||||||
|
{i18n('DELETE_STAFF_MEMBER')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getTicketListProps() {
|
getTicketListProps() {
|
||||||
return {
|
return {
|
||||||
@ -171,7 +219,7 @@ class StaffEditor extends React.Component {
|
|||||||
return SessionStore.getDepartments().map(department => department.name);
|
return SessionStore.getDepartments().map(department => department.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(form) {
|
onSubmit(eventType, form) {
|
||||||
let departments;
|
let departments;
|
||||||
|
|
||||||
if(form.departments) {
|
if(form.departments) {
|
||||||
@ -189,7 +237,29 @@ class StaffEditor extends React.Component {
|
|||||||
level: (form.level !== undefined) ? form.level + 1 : null,
|
level: (form.level !== undefined) ? form.level + 1 : null,
|
||||||
departments: departments && JSON.stringify(departments)
|
departments: departments && JSON.stringify(departments)
|
||||||
}
|
}
|
||||||
}).then(this.props.onChange);
|
}).then(() => {
|
||||||
|
window.scrollTo(0,0);
|
||||||
|
this.setState({message: eventType});
|
||||||
|
|
||||||
|
if(this.props.onChange) {
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
window.scrollTo(0,0);
|
||||||
|
this.setState({message: 'FAIL'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteClick() {
|
||||||
|
API.call({
|
||||||
|
path: '/staff/delete',
|
||||||
|
data: {
|
||||||
|
staffId: this.props.staffId
|
||||||
|
}
|
||||||
|
}).then(this.props.onDelete).catch(() => {
|
||||||
|
window.scrollTo(0,0);
|
||||||
|
this.setState({message: 'FAIL'});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,4 +122,21 @@
|
|||||||
&__separator {
|
&__separator {
|
||||||
margin: 3px 0;
|
margin: 3px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__delete {
|
||||||
|
border: 1px solid $grey;
|
||||||
|
padding: 20px 50px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__delete-title {
|
||||||
|
font-size: $font-size--md;
|
||||||
|
text-align: center;
|
||||||
|
float: left;
|
||||||
|
margin-top: 11px;
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,9 +4,11 @@ import {connect} from 'react-redux';
|
|||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
import AdminDataAction from 'actions/admin-data-actions';
|
import AdminDataAction from 'actions/admin-data-actions';
|
||||||
import Header from 'core-components/header';
|
|
||||||
import TicketList from 'app-components/ticket-list';
|
import TicketList from 'app-components/ticket-list';
|
||||||
|
|
||||||
|
import Header from 'core-components/header';
|
||||||
import SearchBox from 'core-components/search-box';
|
import SearchBox from 'core-components/search-box';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class AdminPanelAllTickets extends React.Component {
|
class AdminPanelAllTickets extends React.Component {
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ class AdminPanelAllTickets extends React.Component {
|
|||||||
<div className="admin-panel-my-tickets__search-box">
|
<div className="admin-panel-my-tickets__search-box">
|
||||||
<SearchBox onSearch={this.onSearch.bind(this)} />
|
<SearchBox onSearch={this.onSearch.bind(this)} />
|
||||||
</div>
|
</div>
|
||||||
<TicketList {...this.getTicketListProps()}/>
|
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getTicketListProps()}/>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -52,7 +54,8 @@ class AdminPanelAllTickets extends React.Component {
|
|||||||
|
|
||||||
onSearch(query) {
|
onSearch(query) {
|
||||||
this.setState({query, page: 1});
|
this.setState({query, page: 1});
|
||||||
if (query) {
|
|
||||||
|
if(query) {
|
||||||
this.props.dispatch(AdminDataAction.searchTickets(query));
|
this.props.dispatch(AdminDataAction.searchTickets(query));
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(AdminDataAction.retrieveAllTickets());
|
this.props.dispatch(AdminDataAction.retrieveAllTickets());
|
||||||
@ -60,9 +63,7 @@ class AdminPanelAllTickets extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onPageChange(event) {
|
onPageChange(event) {
|
||||||
this.setState({
|
this.setState({page: event.target.value});
|
||||||
page: event.target.value
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.state.query) {
|
if(this.state.query) {
|
||||||
this.props.dispatch(AdminDataAction.searchTickets(this.state.query, event.target.value));
|
this.props.dispatch(AdminDataAction.searchTickets(this.state.query, event.target.value));
|
||||||
@ -77,6 +78,7 @@ export default connect((store) => {
|
|||||||
departments: store.session.userDepartments,
|
departments: store.session.userDepartments,
|
||||||
tickets: store.adminData.allTickets,
|
tickets: store.adminData.allTickets,
|
||||||
pages: store.adminData.allTicketsPages,
|
pages: store.adminData.allTicketsPages,
|
||||||
loading: !store.adminData.allTicketsLoaded
|
loading: !store.adminData.allTicketsLoaded,
|
||||||
|
error: store.adminData.allTicketsError
|
||||||
};
|
};
|
||||||
})(AdminPanelAllTickets);
|
})(AdminPanelAllTickets);
|
||||||
|
@ -4,9 +4,11 @@ import {connect} from 'react-redux';
|
|||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
import AdminDataAction from 'actions/admin-data-actions';
|
import AdminDataAction from 'actions/admin-data-actions';
|
||||||
import Header from 'core-components/header';
|
|
||||||
import TicketList from 'app-components/ticket-list';
|
import TicketList from 'app-components/ticket-list';
|
||||||
|
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class AdminPanelMyTickets extends React.Component {
|
class AdminPanelMyTickets extends React.Component {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -22,7 +24,7 @@ class AdminPanelMyTickets extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="admin-panel-my-tickets">
|
<div className="admin-panel-my-tickets">
|
||||||
<Header title={i18n('MY_TICKETS')} description={i18n('MY_TICKETS_DESCRIPTION')} />
|
<Header title={i18n('MY_TICKETS')} description={i18n('MY_TICKETS_DESCRIPTION')} />
|
||||||
<TicketList {...this.getProps()}/>
|
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()}/>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -42,6 +44,7 @@ export default connect((store) => {
|
|||||||
return {
|
return {
|
||||||
departments: store.session.userDepartments,
|
departments: store.session.userDepartments,
|
||||||
tickets: store.adminData.myTickets,
|
tickets: store.adminData.myTickets,
|
||||||
loading: !store.adminData.myTicketsLoaded
|
loading: !store.adminData.myTicketsLoaded,
|
||||||
|
error: store.adminData.myTicketsError
|
||||||
};
|
};
|
||||||
})(AdminPanelMyTickets);
|
})(AdminPanelMyTickets);
|
||||||
|
@ -4,9 +4,11 @@ import {connect} from 'react-redux';
|
|||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
import AdminDataAction from 'actions/admin-data-actions';
|
import AdminDataAction from 'actions/admin-data-actions';
|
||||||
import Header from 'core-components/header';
|
|
||||||
import TicketList from 'app-components/ticket-list';
|
import TicketList from 'app-components/ticket-list';
|
||||||
|
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class AdminPanelNewTickets extends React.Component {
|
class AdminPanelNewTickets extends React.Component {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -22,7 +24,7 @@ class AdminPanelNewTickets extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="admin-panel-my-tickets">
|
<div className="admin-panel-my-tickets">
|
||||||
<Header title={i18n('NEW_TICKETS')} description={i18n('NEW_TICKETS_DESCRIPTION')} />
|
<Header title={i18n('NEW_TICKETS')} description={i18n('NEW_TICKETS_DESCRIPTION')} />
|
||||||
<TicketList {...this.getProps()}/>
|
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()}/>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -42,6 +44,7 @@ export default connect((store) => {
|
|||||||
return {
|
return {
|
||||||
departments: store.session.userDepartments,
|
departments: store.session.userDepartments,
|
||||||
tickets: store.adminData.newTickets,
|
tickets: store.adminData.newTickets,
|
||||||
loading: !store.adminData.newTicketsLoaded
|
loading: !store.adminData.newTicketsLoaded,
|
||||||
|
error: store.adminData.newTicketsError
|
||||||
};
|
};
|
||||||
})(AdminPanelNewTickets);
|
})(AdminPanelNewTickets);
|
||||||
|
@ -11,24 +11,35 @@ import Button from 'core-components/button';
|
|||||||
import SubmitButton from 'core-components/submit-button';
|
import SubmitButton from 'core-components/submit-button';
|
||||||
import Form from 'core-components/form';
|
import Form from 'core-components/form';
|
||||||
import FormField from 'core-components/form-field';
|
import FormField from 'core-components/form-field';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class AdminPanelBanUsers extends React.Component {
|
class AdminPanelBanUsers extends React.Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
loadingList: true,
|
loadingList: true,
|
||||||
loadingForm: false,
|
loadingForm: false,
|
||||||
|
listError: false,
|
||||||
|
addBanStatus: 'none',
|
||||||
emails: [],
|
emails: [],
|
||||||
filteredEmails: []
|
filteredEmails: []
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.retrieveEmails()
|
this.retrieveEmails();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="admin-panel-ban-users row">
|
<div className="admin-panel-ban-users row">
|
||||||
<Header title={i18n('BAN_USERS')} description={i18n('BAN_USERS_DESCRIPTION')} />
|
<Header title={i18n('BAN_USERS')} description={i18n('BAN_USERS_DESCRIPTION')} />
|
||||||
|
{(this.state.listError) ? <Message type="error">{i18n('ERROR_RETRIEVING_BAN_LIST')}</Message> : this.renderContent()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
<div className="admin-panel-ban-users__email-list col-md-6">
|
<div className="admin-panel-ban-users__email-list col-md-6">
|
||||||
<SearchBox className="admin-panel-ban-users__search" onSearch={this.onSearch.bind(this)} searchOnType placeholder={i18n('SEARCH_EMAIL')}/>
|
<SearchBox className="admin-panel-ban-users__search" onSearch={this.onSearch.bind(this)} searchOnType placeholder={i18n('SEARCH_EMAIL')}/>
|
||||||
<Table {...this.getTableProps()} />
|
<Table {...this.getTableProps()} />
|
||||||
@ -41,11 +52,23 @@ class AdminPanelBanUsers extends React.Component {
|
|||||||
<FormField className="admin-panel-ban-users__input" placeholder="email" name="email" validation="EMAIL" required fieldProps={{size: 'large'}}/>
|
<FormField className="admin-panel-ban-users__input" placeholder="email" name="email" validation="EMAIL" required fieldProps={{size: 'large'}}/>
|
||||||
<SubmitButton>{i18n('BAN_EMAIL')}</SubmitButton>
|
<SubmitButton>{i18n('BAN_EMAIL')}</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
|
{this.renderMessage()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderMessage() {
|
||||||
|
switch (this.state.addBanStatus) {
|
||||||
|
case 'success':
|
||||||
|
return <Message className="admin-panel-ban-users__form-message" type="success">{i18n('EMAIL_BANNED_SUCCESSFULLY')}</Message>;
|
||||||
|
case 'fail':
|
||||||
|
return <Message className="admin-panel-ban-users__form-message" type="error">{i18n('ERROR_BANNING_EMAIL')}</Message>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getTableProps() {
|
getTableProps() {
|
||||||
return {
|
return {
|
||||||
loading: this.state.loadingList,
|
loading: this.state.loadingList,
|
||||||
@ -94,7 +117,12 @@ class AdminPanelBanUsers extends React.Component {
|
|||||||
data: {
|
data: {
|
||||||
email: form.email
|
email: form.email
|
||||||
}
|
}
|
||||||
}).then(this.retrieveEmails.bind(this));
|
}).then(() => {
|
||||||
|
this.setState({
|
||||||
|
addBanStatus: 'success'
|
||||||
|
});
|
||||||
|
this.retrieveEmails();
|
||||||
|
}).catch(() => this.setState({addBanStatus: 'fail', loadingForm: false}));
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnBanClick(email) {
|
onUnBanClick(email) {
|
||||||
@ -114,14 +142,17 @@ class AdminPanelBanUsers extends React.Component {
|
|||||||
API.call({
|
API.call({
|
||||||
path: '/user/list-ban',
|
path: '/user/list-ban',
|
||||||
data: {}
|
data: {}
|
||||||
}).then((result) => {
|
}).then(result => this.setState({
|
||||||
this.setState({
|
listError: false,
|
||||||
loadingList: false,
|
loadingList: false,
|
||||||
loadingForm: false,
|
loadingForm: false,
|
||||||
emails: result.data,
|
emails: result.data,
|
||||||
filteredEmails: result.data
|
filteredEmails: result.data
|
||||||
});
|
})).catch(() => this.setState({
|
||||||
});
|
listError: true,
|
||||||
|
loadingList: false,
|
||||||
|
loadingForm: false
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
.admin-panel-ban-users {
|
.admin-panel-ban-users {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|
||||||
&__email-list {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&__search {
|
&__search {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
@ -33,4 +29,8 @@
|
|||||||
&__input {
|
&__input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__form-message {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
@ -8,6 +8,7 @@ import Header from 'core-components/header';
|
|||||||
import Table from 'core-components/table';
|
import Table from 'core-components/table';
|
||||||
import SearchBox from 'core-components/search-box';
|
import SearchBox from 'core-components/search-box';
|
||||||
import Button from 'core-components/button';
|
import Button from 'core-components/button';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class AdminPanelListUsers extends React.Component {
|
class AdminPanelListUsers extends React.Component {
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
users: [],
|
users: [],
|
||||||
orderBy: 'id',
|
orderBy: 'id',
|
||||||
desc: true,
|
desc: true,
|
||||||
|
error: false,
|
||||||
page: 1,
|
page: 1,
|
||||||
pages: 1
|
pages: 1
|
||||||
};
|
};
|
||||||
@ -34,7 +36,7 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
<div className="admin-panel-list-users">
|
<div className="admin-panel-list-users">
|
||||||
<Header title={i18n('LIST_USERS')} description={i18n('LIST_USERS_DESCRIPTION')} />
|
<Header title={i18n('LIST_USERS')} description={i18n('LIST_USERS_DESCRIPTION')} />
|
||||||
<SearchBox className="admin-panel-list-users__search-box" placeholder={i18n('SEARCH_USERS')} onSearch={this.onSearch.bind(this)} />
|
<SearchBox className="admin-panel-list-users__search-box" placeholder={i18n('SEARCH_USERS')} onSearch={this.onSearch.bind(this)} />
|
||||||
<Table {...this.getTableProps()} />
|
{(this.state.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_USERS')}</Message> : <Table {...this.getTableProps()}/>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -144,7 +146,7 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
API.call({
|
API.call({
|
||||||
path: '/user/get-users',
|
path: '/user/get-users',
|
||||||
data: data
|
data: data
|
||||||
}).then(this.onUsersRetrieved.bind(this));
|
}).then(this.onUsersRetrieved.bind(this)).catch(this.onUsersRejected.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
onUsersRetrieved(result) {
|
onUsersRetrieved(result) {
|
||||||
@ -154,6 +156,14 @@ class AdminPanelListUsers extends React.Component {
|
|||||||
users: result.data.users,
|
users: result.data.users,
|
||||||
orderBy: result.data.orderBy,
|
orderBy: result.data.orderBy,
|
||||||
desc: (result.data.desc === '1'),
|
desc: (result.data.desc === '1'),
|
||||||
|
error: false,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUsersRejected() {
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@ import {browserHistory} from 'react-router';
|
|||||||
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 Header from 'core-components/header';
|
|
||||||
import Button from 'core-components/button';
|
|
||||||
import TicketList from 'app-components/ticket-list';
|
import TicketList from 'app-components/ticket-list';
|
||||||
import AreYouSure from 'app-components/are-you-sure';
|
import AreYouSure from 'app-components/are-you-sure';
|
||||||
|
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class AdminPanelViewUser extends React.Component {
|
class AdminPanelViewUser extends React.Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -43,7 +45,7 @@ class AdminPanelViewUser extends React.Component {
|
|||||||
renderInvalid() {
|
renderInvalid() {
|
||||||
return (
|
return (
|
||||||
<div className="admin-panel-view-user__invalid">
|
<div className="admin-panel-view-user__invalid">
|
||||||
{i18n('INVALID_USER')}
|
<Message type="error">{i18n('INVALID_USER')}</Message>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class DashboardListArticlesPage extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div classnames="dashboard-list-articles-page">
|
<div className="dashboard-list-articles-page">
|
||||||
<Header title={i18n('LIST_ARTICLES')} description={i18n('LIST_ARTICLES_DESCRIPTION')}/>
|
<Header title={i18n('LIST_ARTICLES')} description={i18n('LIST_ARTICLES_DESCRIPTION')}/>
|
||||||
<SearchBox className="dashboard-list-articles-page__search-box" onSearch={this.onSearch.bind(this)} searchOnType />
|
<SearchBox className="dashboard-list-articles-page__search-box" onSearch={this.onSearch.bind(this)} searchOnType />
|
||||||
{(!this.state.showSearchResults) ? this.renderArticleList() : this.renderSearchResults()}
|
{(!this.state.showSearchResults) ? this.renderArticleList() : this.renderSearchResults()}
|
||||||
|
@ -19,7 +19,7 @@ class Icon extends React.Component {
|
|||||||
|
|
||||||
renderFontIcon() {
|
renderFontIcon() {
|
||||||
return (
|
return (
|
||||||
<span className={this.getFontIconClass()} aria-hidden="true" style={{color: this.props.color}}/>
|
<span onClick={this.props.onClick} className={this.getFontIconClass()} aria-hidden="true" style={{color: this.props.color}}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class Message extends React.Component {
|
|||||||
getAnimationProps() {
|
getAnimationProps() {
|
||||||
return {
|
return {
|
||||||
defaultStyle: {
|
defaultStyle: {
|
||||||
opacity: spring(0, [100, 30])
|
opacity: 0
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
opacity: spring(1, [100, 30])
|
opacity: spring(1, [100, 30])
|
||||||
|
@ -28,7 +28,7 @@ class Tooltip extends React.Component {
|
|||||||
|
|
||||||
renderAnimatedMessage() {
|
renderAnimatedMessage() {
|
||||||
return (
|
return (
|
||||||
<Motion defaultStyle={{opacity: 0}} style={{opacity: spring(1)}}>
|
<Motion defaultStyle={{opacity: 0, top: -30}} style={{opacity: spring(1), top: spring(0)}}>
|
||||||
{this.renderMessage.bind(this)}
|
{this.renderMessage.bind(this)}
|
||||||
</Motion>
|
</Motion>
|
||||||
)
|
)
|
||||||
@ -36,7 +36,7 @@ class Tooltip extends React.Component {
|
|||||||
|
|
||||||
renderMessage(animation) {
|
renderMessage(animation) {
|
||||||
return (
|
return (
|
||||||
<div style={animation}>
|
<div className="tooltip__animated-container" style={animation}>
|
||||||
<span className="tooltip__pointer-shadow"/>
|
<span className="tooltip__pointer-shadow"/>
|
||||||
<span className="tooltip__pointer"/>
|
<span className="tooltip__pointer"/>
|
||||||
<div className="tooltip__message">
|
<div className="tooltip__message">
|
||||||
|
@ -8,12 +8,15 @@
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__animated-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
&__message {
|
&__message {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: -25%;
|
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
margin-left: -10px;
|
margin-left: -15px;
|
||||||
border: 0 solid rgba(0, 0, 0, 0.247059);
|
border: 0 solid rgba(0, 0, 0, 0.247059);
|
||||||
box-shadow: rgba(0, 0, 0, 0.247059) 0 -1px 4px;
|
box-shadow: rgba(0, 0, 0, 0.247059) 0 -1px 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -1047,5 +1047,15 @@ module.exports = [
|
|||||||
data: {}
|
data: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/staff/delete',
|
||||||
|
time: 100,
|
||||||
|
response: function () {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -147,7 +147,7 @@ module.exports = [
|
|||||||
email: 'kurt@currycurrylady.hs',
|
email: 'kurt@currycurrylady.hs',
|
||||||
tickets: _.times(13).map(() => {
|
tickets: _.times(13).map(() => {
|
||||||
return {
|
return {
|
||||||
ticketNumber: '1185510000',
|
ticketNumber: '118551',
|
||||||
title: 'Lorem ipsum door',
|
title: 'Lorem ipsum door',
|
||||||
content: 'I had a problem with the installation of the php server',
|
content: 'I had a problem with the installation of the php server',
|
||||||
department: {
|
department: {
|
||||||
@ -368,7 +368,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
ticketNumber: '445441',
|
ticketNumber: '445441',
|
||||||
title: 'Problem with installation',
|
title: 'Problem with installation',
|
||||||
content: 'I had a problem with the installation of the php server',
|
content: 'In varius, tellus ut luctus vestibulum, orci erat commodo ligula, sit amet bibendum arcu libero sed magna. Suspendisse in ligula vitae ante placerat varius id in eros. Etiam commodo viverra nisi in ornare. Donec ullamcorper felis sapien, eu laoreet dolor tincidunt nec. Aliquam erat volutpat. Proin semper viverra purus eget facilisis. Proin fermentum et odio in elementum. Maecenas lacinia, massa consectetur gravida lacinia, nisl lectus tincidunt diam, ut viverra ipsum ex sit amet diam. Mauris ac massa turpis. Fusce ultrices venenatis vestibulum. In et nulla purus. Nullam porta vestibulum leo in dignissim. Duis id ullamcorper odio. Ut purus nulla, consequat lobortis volutpat quis, consequat et libero. Maecenas sit amet libero laoreet, dictum sapien at, hendrerit sapien.',
|
||||||
department: {
|
department: {
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Technical Issues'
|
name: 'Technical Issues'
|
||||||
@ -502,9 +502,7 @@ module.exports = [
|
|||||||
name: 'Haskell Curry',
|
name: 'Haskell Curry',
|
||||||
email: 'haskell@lambda.com'
|
email: 'haskell@lambda.com'
|
||||||
},
|
},
|
||||||
owner: {
|
owner: null,
|
||||||
name: 'Steve Jobs'
|
|
||||||
},
|
|
||||||
events: [
|
events: [
|
||||||
{
|
{
|
||||||
type: 'ASSIGN',
|
type: 'ASSIGN',
|
||||||
@ -516,17 +514,6 @@ module.exports = [
|
|||||||
staff: true
|
staff: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'COMMENT',
|
|
||||||
date: '201504090912',
|
|
||||||
content: 'Do you have apache installed? It generally happens if you dont have apache.',
|
|
||||||
author: {
|
|
||||||
name: 'Emilia Clarke',
|
|
||||||
email: 'jobs@steve.com',
|
|
||||||
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
|
||||||
staff: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'UN_ASSIGN',
|
type: 'UN_ASSIGN',
|
||||||
date: '201504100924',
|
date: '201504100924',
|
||||||
|
@ -30,6 +30,7 @@ export default {
|
|||||||
'STAFF': 'Staff',
|
'STAFF': 'Staff',
|
||||||
'CUSTOMER': 'Customer',
|
'CUSTOMER': 'Customer',
|
||||||
'YES': 'Yes',
|
'YES': 'Yes',
|
||||||
|
'NO': 'No',
|
||||||
'CANCEL': 'Cancel',
|
'CANCEL': 'Cancel',
|
||||||
'MY_ACCOUNT': 'My Account',
|
'MY_ACCOUNT': 'My Account',
|
||||||
'DASHBOARD': 'Dashboard',
|
'DASHBOARD': 'Dashboard',
|
||||||
@ -114,6 +115,8 @@ export default {
|
|||||||
'ADD_DEPARTMENT': 'Add department',
|
'ADD_DEPARTMENT': 'Add department',
|
||||||
'UPDATE_DEPARTMENT': 'Update department',
|
'UPDATE_DEPARTMENT': 'Update department',
|
||||||
'TRANSFER_TICKETS_TO': 'Transfer tickets to',
|
'TRANSFER_TICKETS_TO': 'Transfer tickets to',
|
||||||
|
'COMMENTS': 'Comments',
|
||||||
|
'DELETE_STAFF_MEMBER': 'Delete staff member',
|
||||||
|
|
||||||
//VIEW DESCRIPTIONS
|
//VIEW DESCRIPTIONS
|
||||||
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',
|
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',
|
||||||
@ -136,6 +139,7 @@ export default {
|
|||||||
'ADD_ARTICLE_DESCRIPTION': 'Here you can add an article that will be available for every user. It will be added inside the category {category}.',
|
'ADD_ARTICLE_DESCRIPTION': 'Here you can add an article that will be available for every user. It will be added inside the category {category}.',
|
||||||
'LIST_ARTICLES_DESCRIPTION': 'This is a list of articles that includes information about our services.',
|
'LIST_ARTICLES_DESCRIPTION': 'This is a list of articles that includes information about our services.',
|
||||||
'ADD_TOPIC_DESCRIPTION': 'Here you can add a topic that works as a category for articles.',
|
'ADD_TOPIC_DESCRIPTION': 'Here you can add a topic that works as a category for articles.',
|
||||||
|
'DELETE_ARTICLE_DESCRIPTION': 'You\'re going to delete this article for ever.',
|
||||||
'STAFF_MEMBERS_DESCRIPTION': 'Here you can see who are your staff members.',
|
'STAFF_MEMBERS_DESCRIPTION': 'Here you can see who are your staff members.',
|
||||||
'ADD_STAFF_DESCRIPTION': 'Here you can add staff members to your teams.',
|
'ADD_STAFF_DESCRIPTION': 'Here you can add staff members to your teams.',
|
||||||
'EDIT_STAFF_DESCRIPTION': 'Here you can edit information about a staff member.',
|
'EDIT_STAFF_DESCRIPTION': 'Here you can edit information about a staff member.',
|
||||||
@ -154,8 +158,14 @@ export default {
|
|||||||
'PASSWORD_NOT_MATCH': 'Password does not match',
|
'PASSWORD_NOT_MATCH': 'Password does not match',
|
||||||
'INVALID_RECOVER': 'Invalid recover data',
|
'INVALID_RECOVER': 'Invalid recover data',
|
||||||
'TICKET_SENT_ERROR': 'An error occurred while trying to create the ticket.',
|
'TICKET_SENT_ERROR': 'An error occurred while trying to create the ticket.',
|
||||||
|
'TICKET_COMMENT_ERROR': 'An error occurred while trying to add the comment.',
|
||||||
'NO_PERMISSION': 'You\'ve no permission to access to this page.',
|
'NO_PERMISSION': 'You\'ve no permission to access to this page.',
|
||||||
'INVALID_USER': 'User id is invalid',
|
'INVALID_USER': 'User id is invalid',
|
||||||
|
'ERROR_RETRIEVING_TICKETS': 'An error occurred while trying to retrieve tickets.',
|
||||||
|
'ERROR_RETRIEVING_USERS': 'An error occurred while trying to retrieve users.',
|
||||||
|
'ERROR_RETRIEVING_BAN_LIST': 'An error occurred while trying to retrieve the list of banned emails.',
|
||||||
|
'ERROR_BANNING_EMAIL': 'An error occurred while trying to ban the email.',
|
||||||
|
'ERROR_RETRIEVING_ARTICLES': 'An error occurred while trying to retrieve articles.',
|
||||||
|
|
||||||
//MESSAGES
|
//MESSAGES
|
||||||
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',
|
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',
|
||||||
@ -169,5 +179,10 @@ export default {
|
|||||||
'WILL_LOSE_CHANGES': 'You haven\'t save. Your changes will be lost.',
|
'WILL_LOSE_CHANGES': 'You haven\'t save. Your changes will be lost.',
|
||||||
'WILL_DELETE_CUSTOM_RESPONSE': 'The custom response will be deleted.',
|
'WILL_DELETE_CUSTOM_RESPONSE': 'The custom response will be deleted.',
|
||||||
'WILL_DELETE_DEPARTMENT': 'The department will be deleted. All the tickets will be transfer to the department selected.',
|
'WILL_DELETE_DEPARTMENT': 'The department will be deleted. All the tickets will be transfer to the department selected.',
|
||||||
'NO_STAFF_ASSIGNED': 'No staff member is assigned to this department.'
|
'NO_STAFF_ASSIGNED': 'No staff member is assigned to this department.',
|
||||||
|
'LEVEL_UPDATED': 'Level has been updated successfully.',
|
||||||
|
'DEPARTMENTS_UPDATED': 'Departments have been updated successfully.',
|
||||||
|
'FAILED_EDIT_STAFF': 'An error occurred while trying to edit staff member.',
|
||||||
|
'EMAIL_BANNED_SUCCESSFULLY': 'Email has been banned successfully',
|
||||||
|
'WILL_DELETE_STAFF': 'This staff member will be deleted and all its tickets will be unassigned.'
|
||||||
};
|
};
|
||||||
|
@ -11,10 +11,15 @@ class AdminDataReducer extends Reducer {
|
|||||||
customResponsesLoaded: false,
|
customResponsesLoaded: false,
|
||||||
myTickets: [],
|
myTickets: [],
|
||||||
myTicketsLoaded: false,
|
myTicketsLoaded: false,
|
||||||
|
myTicketsError: false,
|
||||||
|
|
||||||
newTickets: [],
|
newTickets: [],
|
||||||
newTicketsLoaded: false,
|
newTicketsLoaded: false,
|
||||||
|
newTicketsError: false,
|
||||||
|
|
||||||
allTickets: [],
|
allTickets: [],
|
||||||
allTicketsLoaded: false
|
allTicketsLoaded: false,
|
||||||
|
allTicketsError: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,11 +27,17 @@ class AdminDataReducer extends Reducer {
|
|||||||
return {
|
return {
|
||||||
'CUSTOM_RESPONSES_FULFILLED': this.onCustomResponses,
|
'CUSTOM_RESPONSES_FULFILLED': this.onCustomResponses,
|
||||||
'SESSION_CHECKED': this.onSessionChecked,
|
'SESSION_CHECKED': this.onSessionChecked,
|
||||||
|
|
||||||
'MY_TICKETS_FULFILLED': this.onMyTicketsRetrieved,
|
'MY_TICKETS_FULFILLED': this.onMyTicketsRetrieved,
|
||||||
|
'MY_TICKETS_REJECTED': this.onMyTicketsRejected,
|
||||||
'MY_TICKETS_PENDING': this.onMyTicketsPending,
|
'MY_TICKETS_PENDING': this.onMyTicketsPending,
|
||||||
|
|
||||||
'NEW_TICKETS_FULFILLED': this.onNewTicketsRetrieved,
|
'NEW_TICKETS_FULFILLED': this.onNewTicketsRetrieved,
|
||||||
|
'NEW_TICKETS_REJECTED': this.onNewTicketsRejected,
|
||||||
'NEW_TICKETS_PENDING': this.onNewTicketsPending,
|
'NEW_TICKETS_PENDING': this.onNewTicketsPending,
|
||||||
|
|
||||||
'ALL_TICKETS_FULFILLED': this.onAllTicketsRetrieved,
|
'ALL_TICKETS_FULFILLED': this.onAllTicketsRetrieved,
|
||||||
|
'ALL_TICKETS_REJECTED': this.onAllTicketsRejected,
|
||||||
'ALL_TICKETS_PENDING': this.onAllTicketsPending
|
'ALL_TICKETS_PENDING': this.onAllTicketsPending
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -53,26 +64,42 @@ class AdminDataReducer extends Reducer {
|
|||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
myTickets: payload.data,
|
myTickets: payload.data,
|
||||||
myTicketsLoaded: true
|
myTicketsLoaded: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMyTicketsRejected(state) {
|
||||||
|
return _.extend({}, state, {
|
||||||
|
myTicketsError: true,
|
||||||
|
myTicketsLoaded: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMyTicketsPending(state) {
|
onMyTicketsPending(state) {
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
|
myTicketsError: false,
|
||||||
myTicketsLoaded: false
|
myTicketsLoaded: false
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewTicketsRetrieved(state, payload) {
|
onNewTicketsRetrieved(state, payload) {
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
newTickets: payload.data,
|
newTickets: payload.data,
|
||||||
newTicketsLoaded: true
|
newTicketsLoaded: true
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewTicketsRejected(state) {
|
||||||
|
return _.extend({}, state, {
|
||||||
|
newTicketsError: true,
|
||||||
|
newTicketsLoaded: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewTicketsPending(state) {
|
onNewTicketsPending(state) {
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
|
newTicketsError: false,
|
||||||
newTicketsLoaded: false
|
newTicketsLoaded: false
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onAllTicketsRetrieved(state, payload) {
|
onAllTicketsRetrieved(state, payload) {
|
||||||
@ -80,13 +107,21 @@ class AdminDataReducer extends Reducer {
|
|||||||
allTickets: payload.data.tickets,
|
allTickets: payload.data.tickets,
|
||||||
allTicketsPages: payload.data.pages,
|
allTicketsPages: payload.data.pages,
|
||||||
allTicketsLoaded: true
|
allTicketsLoaded: true
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onAllTicketsRejected(state) {
|
||||||
|
return _.extend({}, state, {
|
||||||
|
allTicketsError: false,
|
||||||
|
allTicketsLoaded: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onAllTicketsPending(state) {
|
onAllTicketsPending(state) {
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
|
allTicketsError: false,
|
||||||
allTicketsLoaded: false
|
allTicketsLoaded: false
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ class ArticlesReducer extends Reducer {
|
|||||||
return {
|
return {
|
||||||
retrieved: false,
|
retrieved: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
errored: false,
|
||||||
topics: []
|
topics: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -16,6 +17,7 @@ class ArticlesReducer extends Reducer {
|
|||||||
getTypeHandlers() {
|
getTypeHandlers() {
|
||||||
return {
|
return {
|
||||||
'GET_ARTICLES_FULFILLED': this.onArticlesRetrieved,
|
'GET_ARTICLES_FULFILLED': this.onArticlesRetrieved,
|
||||||
|
'GET_ARTICLES_REJECTED': this.onArticlesRejected,
|
||||||
'INIT_ARTICLES': this.onInitArticles
|
'INIT_ARTICLES': this.onInitArticles
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -26,10 +28,19 @@ class ArticlesReducer extends Reducer {
|
|||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
retrieved: true,
|
retrieved: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
errored: false,
|
||||||
topics: payload.data
|
topics: payload.data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onArticlesRejected(state) {
|
||||||
|
return _.extend({}, state, {
|
||||||
|
retrieved: true,
|
||||||
|
loading: false,
|
||||||
|
errored: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onInitArticles(state) {
|
onInitArticles(state) {
|
||||||
let topics = SessionStore.getItem('topics');
|
let topics = SessionStore.getItem('topics');
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"slim/slim": "~2.0",
|
"slim/slim": "~2.0",
|
||||||
"gabordemooij/redbean": "~4.2",
|
|
||||||
"respect/validation": "^1.1",
|
"respect/validation": "^1.1",
|
||||||
"phpmailer/phpmailer": "^5.2",
|
"phpmailer/phpmailer": "^5.2",
|
||||||
"google/recaptcha": "~1.1"
|
"google/recaptcha": "~1.1",
|
||||||
|
"gabordemooij/redbean": "^4.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "5.0.*"
|
"phpunit/phpunit": "5.0.*"
|
||||||
|
@ -23,6 +23,11 @@ class DeleteStaffController extends Controller {
|
|||||||
$staffId = Controller::request('staffId');
|
$staffId = Controller::request('staffId');
|
||||||
$staff = Staff::getDataStore($staffId);
|
$staff = Staff::getDataStore($staffId);
|
||||||
|
|
||||||
|
if($staffId === Controller::getLoggedUser()->id) {
|
||||||
|
Response::respondError(ERRORS::INVALID_STAFF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach($staff->sharedTicketList as $ticket) {
|
foreach($staff->sharedTicketList as $ticket) {
|
||||||
$ticket->owner = null;
|
$ticket->owner = null;
|
||||||
$ticket->true = true;
|
$ticket->true = true;
|
||||||
|
@ -4,8 +4,7 @@ use Respect\Validation\Validator as DataValidator;
|
|||||||
class EditStaffController extends Controller {
|
class EditStaffController extends Controller {
|
||||||
const PATH = '/edit';
|
const PATH = '/edit';
|
||||||
|
|
||||||
private $staffRow;
|
private $staffInstance;
|
||||||
private $staffId;
|
|
||||||
|
|
||||||
public function validations() {
|
public function validations() {
|
||||||
return [
|
return [
|
||||||
@ -15,14 +14,14 @@ class EditStaffController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function handler() {
|
public function handler() {
|
||||||
$this->staffId = Controller::request('staffId');
|
$staffId = Controller::request('staffId');
|
||||||
|
|
||||||
if(!$this->staffId) {
|
if(!$staffId) {
|
||||||
$this->staffRow = Controller::getLoggedUser();
|
$this->staffInstance = Controller::getLoggedUser();
|
||||||
} else if(Controller::isStaffLogged(3)) {
|
} else if(Controller::isStaffLogged(3)) {
|
||||||
$this->staffRow = Staff::getDataStore($this->staffId, 'id');
|
$this->staffInstance = Staff::getDataStore($staffId, 'id');
|
||||||
|
|
||||||
if($this->staffRow->isNull()) {
|
if($this->staffInstance->isNull()) {
|
||||||
Response::respondError(ERRORS::INVALID_STAFF);
|
Response::respondError(ERRORS::INVALID_STAFF);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -39,29 +38,29 @@ class EditStaffController extends Controller {
|
|||||||
Response::respondSuccess();
|
Response::respondSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function editInformation() {
|
private function editInformation() {
|
||||||
|
|
||||||
if(Controller::request('email')) {
|
if(Controller::request('email')) {
|
||||||
$this->staffRow->email = Controller::request('email');
|
$this->staffInstance->email = Controller::request('email');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Controller::request('password')) {
|
if(Controller::request('password')) {
|
||||||
$this->staffRow->password = Hashing::hashPassword(Controller::request('password'));
|
$this->staffInstance->password = Hashing::hashPassword(Controller::request('password'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Controller::request('level') && Controller::isStaffLogged(3)) {
|
if(Controller::request('level') && Controller::isStaffLogged(3) && Controller::request('staffId') !== Controller::getLoggedUser()->id) {
|
||||||
$this->staffRow->level = Controller::request('level');
|
$this->staffInstance->level = Controller::request('level');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Controller::request('departments') && Controller::isStaffLogged(3)) {
|
if(Controller::request('departments') && Controller::isStaffLogged(3)) {
|
||||||
$this->staffRow->sharedDepartmentList = $this->getDepartmentList();
|
$this->staffInstance->sharedDepartmentList = $this->getDepartmentList();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->staffRow->store();
|
$this->staffInstance->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getDepartmentList() {
|
private function getDepartmentList() {
|
||||||
$listDepartments = new DataStoreList();
|
$listDepartments = new DataStoreList();
|
||||||
$departmentIds = json_decode(Controller::request('departments'));
|
$departmentIds = json_decode(Controller::request('departments'));
|
||||||
|
|
||||||
@ -73,8 +72,8 @@ class EditStaffController extends Controller {
|
|||||||
return $listDepartments;
|
return $listDepartments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateDepartmentsOwners() {
|
private function updateDepartmentsOwners() {
|
||||||
$list1 = $this->staffRow->sharedDepartmentList;
|
$list1 = $this->staffInstance->sharedDepartmentList;
|
||||||
$list2 = $this->getDepartmentList();
|
$list2 = $this->getDepartmentList();
|
||||||
|
|
||||||
foreach ($list1 as $department1) {
|
foreach ($list1 as $department1) {
|
||||||
|
@ -13,7 +13,7 @@ class CommentController extends Controller {
|
|||||||
'permission' => 'user',
|
'permission' => 'user',
|
||||||
'requestData' => [
|
'requestData' => [
|
||||||
'content' => [
|
'content' => [
|
||||||
'validation' => DataValidator::length(20, 500),
|
'validation' => DataValidator::length(20, 5000),
|
||||||
'error' => ERRORS::INVALID_CONTENT
|
'error' => ERRORS::INVALID_CONTENT
|
||||||
],
|
],
|
||||||
'ticketNumber' => [
|
'ticketNumber' => [
|
||||||
|
@ -20,7 +20,7 @@ class CreateController extends Controller {
|
|||||||
'error' => ERRORS::INVALID_TITLE
|
'error' => ERRORS::INVALID_TITLE
|
||||||
],
|
],
|
||||||
'content' => [
|
'content' => [
|
||||||
'validation' => DataValidator::length(10, 500),
|
'validation' => DataValidator::length(10, 5000),
|
||||||
'error' => ERRORS::INVALID_CONTENT
|
'error' => ERRORS::INVALID_CONTENT
|
||||||
],
|
],
|
||||||
'departmentId' => [
|
'departmentId' => [
|
||||||
|
@ -14,6 +14,7 @@ include 'user/delete.php';
|
|||||||
include 'user/ban.php';
|
include 'user/ban.php';
|
||||||
include 'user/un-ban.php';
|
include 'user/un-ban.php';
|
||||||
include 'user/list-ban.php';
|
include 'user/list-ban.php';
|
||||||
|
include 'user/verify.php';
|
||||||
|
|
||||||
$userControllers = new ControllerGroup();
|
$userControllers = new ControllerGroup();
|
||||||
$userControllers->setGroupPath('/user');
|
$userControllers->setGroupPath('/user');
|
||||||
@ -33,4 +34,5 @@ $userControllers->addController(new DeleteUserController);
|
|||||||
$userControllers->addController(new BanUserController);
|
$userControllers->addController(new BanUserController);
|
||||||
$userControllers->addController(new UnBanUserController);
|
$userControllers->addController(new UnBanUserController);
|
||||||
$userControllers->addController(new ListBanUserController);
|
$userControllers->addController(new ListBanUserController);
|
||||||
|
$userControllers->addController(new VerifyController);
|
||||||
$userControllers->finalize();
|
$userControllers->finalize();
|
||||||
|
@ -34,7 +34,8 @@ class GetUserByIdController extends Controller {
|
|||||||
'name' => $user->name,
|
'name' => $user->name,
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
'signupDate' => $user->signupDate,
|
'signupDate' => $user->signupDate,
|
||||||
'tickets' => $tickets->toArray()
|
'tickets' => $tickets->toArray(),
|
||||||
|
'verified' => !$user->verificationToken
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,6 +27,14 @@ class LoginController extends Controller {
|
|||||||
$this->userInstance->store();
|
$this->userInstance->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$email = Controller::request('email');
|
||||||
|
$userRow = User::getDataStore($email, 'email');
|
||||||
|
|
||||||
|
if($userRow->verificationToken !== null) {
|
||||||
|
Response::respondError(ERRORS::UNVERIFIED_USER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Response::respondSuccess($this->getUserData());
|
Response::respondSuccess($this->getUserData());
|
||||||
} else {
|
} else {
|
||||||
Response::respondError(ERRORS::INVALID_CREDENTIALS);
|
Response::respondError(ERRORS::INVALID_CREDENTIALS);
|
||||||
|
@ -9,6 +9,7 @@ class SignUpController extends Controller {
|
|||||||
private $userEmail;
|
private $userEmail;
|
||||||
private $userName;
|
private $userName;
|
||||||
private $userPassword;
|
private $userPassword;
|
||||||
|
private $verificationToken;
|
||||||
|
|
||||||
public function validations() {
|
public function validations() {
|
||||||
return [
|
return [
|
||||||
@ -65,17 +66,19 @@ class SignUpController extends Controller {
|
|||||||
$this->userName = Controller::request('name');
|
$this->userName = Controller::request('name');
|
||||||
$this->userEmail = Controller::request('email');
|
$this->userEmail = Controller::request('email');
|
||||||
$this->userPassword = Controller::request('password');
|
$this->userPassword = Controller::request('password');
|
||||||
|
$this->verificationToken = Hashing::generateRandomToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createNewUserAndRetrieveId() {
|
public function createNewUserAndRetrieveId() {
|
||||||
$userInstance = new User();
|
$userInstance = new User();
|
||||||
|
|
||||||
$userInstance->setProperties([
|
$userInstance->setProperties([
|
||||||
'name' => $this->userName,
|
'name' => $this->userName,
|
||||||
'signupDate' => Date::getCurrentDate(),
|
'signupDate' => Date::getCurrentDate(),
|
||||||
'tickets' => 0,
|
'tickets' => 0,
|
||||||
'email' => $this->userEmail,
|
'email' => $this->userEmail,
|
||||||
'password' => Hashing::hashPassword($this->userPassword)
|
'password' => Hashing::hashPassword($this->userPassword),
|
||||||
|
'verificationToken' => $this->verificationToken
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $userInstance->store();
|
return $userInstance->store();
|
||||||
@ -86,7 +89,8 @@ class SignUpController extends Controller {
|
|||||||
|
|
||||||
$mailSender->setTemplate(MailTemplate::USER_SIGNUP, [
|
$mailSender->setTemplate(MailTemplate::USER_SIGNUP, [
|
||||||
'to' => $this->userEmail,
|
'to' => $this->userEmail,
|
||||||
'name' => $this->userName
|
'name' => $this->userName,
|
||||||
|
'verificationToken' => $this->verificationToken
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$mailSender->send();
|
$mailSender->send();
|
||||||
|
38
server/controllers/user/verify.php
Normal file
38
server/controllers/user/verify.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
|
||||||
|
class VerifyController extends Controller{
|
||||||
|
const PATH = '/verify';
|
||||||
|
|
||||||
|
public function validations() {
|
||||||
|
return [
|
||||||
|
'permission' => 'any',
|
||||||
|
'requestData' => [
|
||||||
|
'email' => [
|
||||||
|
'validation' => DataValidator::email(),
|
||||||
|
'error' => ERRORS::INVALID_EMAIL
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handler() {
|
||||||
|
$email = Controller::request('email');
|
||||||
|
$token = Controller::request('token');
|
||||||
|
|
||||||
|
$userRow = User::getDataStore($email, 'email');
|
||||||
|
|
||||||
|
if(!$userRow) {
|
||||||
|
Response::respondError(ERRORS::INVALID_EMAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if($userRow->verificationToken !== $token) {
|
||||||
|
Response::respondError(ERRORS::INVALID_TOKEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$userRow->verificationToken = null;
|
||||||
|
$userRow->store();
|
||||||
|
|
||||||
|
Response::respondSuccess();
|
||||||
|
}
|
||||||
|
}
|
@ -30,4 +30,6 @@ class ERRORS {
|
|||||||
const ALREADY_A_STAFF = 'ALREADY_A_STAFF';
|
const ALREADY_A_STAFF = 'ALREADY_A_STAFF';
|
||||||
const INVALID_STAFF = 'INVALID_STAFF';
|
const INVALID_STAFF = 'INVALID_STAFF';
|
||||||
const SAME_DEPARTMENT = 'SAME_DEPARTMENT';
|
const SAME_DEPARTMENT = 'SAME_DEPARTMENT';
|
||||||
|
const INVALID_TOKEN = 'INVALID_TOKEN';
|
||||||
|
const UNVERIFIED_USER = 'UNVERIFIED_USER';
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<div>
|
<div>
|
||||||
Welcome, {{name}} to our support center,
|
Welcome, {{name}} to our support center,
|
||||||
your email is {{to}}
|
your email is {{to}},
|
||||||
|
your token is {{verificationToken}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<div>
|
<div>
|
||||||
Bienvenido, {{name}} a nuestro centro de soporte,
|
Bienvenido, {{name}} a nuestro centro de soporte,
|
||||||
tu email es {{to}}
|
tu email es {{to}},
|
||||||
|
tu codigo de verificacion es {{verificationToken}}
|
||||||
</div>
|
</div>
|
@ -17,7 +17,8 @@ class User extends DataStore {
|
|||||||
'name',
|
'name',
|
||||||
'signupDate',
|
'signupDate',
|
||||||
'tickets',
|
'tickets',
|
||||||
'sharedTicketList'
|
'sharedTicketList',
|
||||||
|
'verificationToken'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,11 @@ class Scripts
|
|||||||
if response['status'] === 'fail'
|
if response['status'] === 'fail'
|
||||||
raise response['message']
|
raise response['message']
|
||||||
end
|
end
|
||||||
|
userRow = $database.getRow('user', email, 'email')
|
||||||
|
response = request('/user/verify', {
|
||||||
|
:email => email,
|
||||||
|
:token => userRow['verification_token']
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.login(email = 'steve@jobs.com', password = 'custompassword', staff = false)
|
def self.login(email = 'steve@jobs.com', password = 'custompassword', staff = false)
|
||||||
|
@ -1,104 +1,85 @@
|
|||||||
describe'system/edit-settings' do
|
describe'system/edit-settings' do
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
|
it 'should edit settings' do
|
||||||
|
result= request('/system/edit-settings', {
|
||||||
|
"csrf_userid" => $csrf_userid,
|
||||||
|
"csrf_token" => $csrf_token,
|
||||||
|
"maintenance-mode" => 1,
|
||||||
|
"time-zone" => -3,
|
||||||
|
"layout" => 'full-width',
|
||||||
|
"allow-attachments" => 1,
|
||||||
|
"max-size" => 2,
|
||||||
|
"language" => 'es',
|
||||||
|
"no-reply-email" => 'testemail@hotmail.com'
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
|
row = $database.getRow('setting', 'maintenance-mode', 'name')
|
||||||
|
(row['value']).should.equal('1')
|
||||||
|
|
||||||
|
row = $database.getRow('setting', 'time-zone', 'name')
|
||||||
|
(row['value']).should.equal('-3')
|
||||||
|
|
||||||
|
row = $database.getRow('setting', 'layout', 'name')
|
||||||
|
(row['value']).should.equal('full-width')
|
||||||
|
|
||||||
|
row = $database.getRow('setting', 'max-size', 'name')
|
||||||
|
(row['value']).should.equal('2')
|
||||||
|
|
||||||
|
row = $database.getRow('setting', 'language', 'name')
|
||||||
|
(row['value']).should.equal('es')
|
||||||
|
|
||||||
|
row = $database.getRow('setting', 'no-reply-email', 'name')
|
||||||
|
(row['value']).should.equal('testemail@hotmail.com')
|
||||||
|
|
||||||
|
request('/user/logout')
|
||||||
|
end
|
||||||
|
it 'should change allowed and supported languages' do
|
||||||
request('/user/logout')
|
request('/user/logout')
|
||||||
Scripts.login($staff[:email], $staff[:password], true)
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
it 'should edit settings' do
|
result= request('/system/edit-settings', {
|
||||||
result= request('/system/edit-settings', {
|
"csrf_userid" => $csrf_userid,
|
||||||
"csrf_userid" => $csrf_userid,
|
"csrf_token" => $csrf_token,
|
||||||
"csrf_token" => $csrf_token,
|
"supportedLanguages" => '["en", "pr", "jp", "ru"]',
|
||||||
"maintenance-mode" => 1,
|
"allowedLanguages" => '["en","pr", "jp", "ru", "de"]'
|
||||||
"time-zone" => -3,
|
})
|
||||||
"layout" => 'full-width',
|
|
||||||
"allow-attachments" => 1,
|
|
||||||
"max-size" => 2,
|
|
||||||
"language" => 'es',
|
|
||||||
"no-reply-email" => 'testemail@hotmail.com',
|
|
||||||
"smtp-host" => 'www.opensupports.com',
|
|
||||||
"smtp-port" => 18,
|
|
||||||
"smtp-user" => 'admin',
|
|
||||||
"smtp-pass" => 'pass1234',
|
|
||||||
})
|
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'maintenance-mode', 'name')
|
row = $database.getRow('language', 'en', 'code')
|
||||||
(row['value']).should.equal('1')
|
(row['supported']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'time-zone', 'name')
|
row = $database.getRow('language', 'pr', 'code')
|
||||||
(row['value']).should.equal('-3')
|
(row['supported']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'layout', 'name')
|
row = $database.getRow('language', 'jp', 'code')
|
||||||
(row['value']).should.equal('full-width')
|
(row['supported']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'max-size', 'name')
|
row = $database.getRow('language', 'ru', 'code')
|
||||||
(row['value']).should.equal('2')
|
(row['supported']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'language', 'name')
|
row = $database.getRow('language', 'en', 'code')
|
||||||
(row['value']).should.equal('es')
|
(row['allowed']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'no-reply-email', 'name')
|
row = $database.getRow('language', 'pr', 'code')
|
||||||
(row['value']).should.equal('testemail@hotmail.com')
|
(row['allowed']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'smtp-host', 'name')
|
row = $database.getRow('language', 'jp', 'code')
|
||||||
(row['value']).should.equal('www.opensupports.com')
|
(row['allowed']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'smtp-port', 'name')
|
row = $database.getRow('language', 'ru', 'code')
|
||||||
(row['value']).should.equal('18')
|
(row['allowed']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'smtp-user', 'name')
|
row = $database.getRow('language', 'de', 'code')
|
||||||
(row['value']).should.equal('admin')
|
(row['allowed']).should.equal('1')
|
||||||
|
|
||||||
row = $database.getRow('setting', 'smtp-pass', 'name')
|
lastLog = $database.getLastRow('log')
|
||||||
(row['value']).should.equal('pass1234')
|
(lastLog['type']).should.equal('EDIT_SETTINGS')
|
||||||
|
|
||||||
request('/user/logout')
|
request('/user/logout')
|
||||||
|
end
|
||||||
lastLog = $database.getLastRow('log')
|
end
|
||||||
(lastLog['type']).should.equal('EDIT_SETTINGS')
|
|
||||||
end
|
|
||||||
it 'should change allowed and supported languages' do
|
|
||||||
request('/user/logout')
|
|
||||||
Scripts.login($staff[:email], $staff[:password], true)
|
|
||||||
|
|
||||||
result= request('/system/edit-settings', {
|
|
||||||
"csrf_userid" => $csrf_userid,
|
|
||||||
"csrf_token" => $csrf_token,
|
|
||||||
"supportedLanguages" => '["en", "pr", "jp", "ru"]',
|
|
||||||
"allowedLanguages" => '["en","pr", "jp", "ru", "de"]'
|
|
||||||
})
|
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'en', 'code')
|
|
||||||
(row['supported']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'pr', 'code')
|
|
||||||
(row['supported']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'jp', 'code')
|
|
||||||
(row['supported']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'ru', 'code')
|
|
||||||
(row['supported']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'en', 'code')
|
|
||||||
(row['allowed']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'pr', 'code')
|
|
||||||
(row['allowed']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'jp', 'code')
|
|
||||||
(row['allowed']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'ru', 'code')
|
|
||||||
(row['allowed']).should.equal('1')
|
|
||||||
|
|
||||||
row = $database.getRow('language', 'de', 'code')
|
|
||||||
(row['allowed']).should.equal('1')
|
|
||||||
|
|
||||||
lastLog = $database.getLastRow('log')
|
|
||||||
(lastLog['type']).should.equal('EDIT_SETTINGS')
|
|
||||||
|
|
||||||
request('/user/logout')
|
|
||||||
end
|
|
||||||
end
|
|
@ -32,7 +32,7 @@ describe '/ticket/comment/' do
|
|||||||
|
|
||||||
it 'should fail if content is very long' do
|
it 'should fail if content is very long' do
|
||||||
long_text = ''
|
long_text = ''
|
||||||
600.times {long_text << 'a'}
|
6000.times {long_text << 'a'}
|
||||||
|
|
||||||
result = request('/ticket/comment', {
|
result = request('/ticket/comment', {
|
||||||
content: long_text,
|
content: long_text,
|
||||||
|
@ -62,7 +62,7 @@ describe '/ticket/create' do
|
|||||||
|
|
||||||
it 'should fail if content is very long' do
|
it 'should fail if content is very long' do
|
||||||
long_text = ''
|
long_text = ''
|
||||||
600.times {long_text << 'a'}
|
6000.times {long_text << 'a'}
|
||||||
|
|
||||||
result = request('/ticket/create',{
|
result = request('/ticket/create',{
|
||||||
title: 'Winter is coming',
|
title: 'Winter is coming',
|
||||||
|
@ -4,6 +4,7 @@ describe '/user/get-users' do
|
|||||||
Scripts.createUser('tests@hotmail.com','passdasdasdas','laasdasd')
|
Scripts.createUser('tests@hotmail.com','passdasdasdas','laasdasd')
|
||||||
Scripts.createUser('tests2@hotmail.com','passfasfasfsa','laeaefae')
|
Scripts.createUser('tests2@hotmail.com','passfasfasfsa','laeaefae')
|
||||||
Scripts.createUser('tests3@hotmail.com','passfasfasfws','laeczvwaf')
|
Scripts.createUser('tests3@hotmail.com','passfasfasfws','laeczvwaf')
|
||||||
|
|
||||||
result = request('/user/login', {
|
result = request('/user/login', {
|
||||||
email: 'staff@opensupports.com',
|
email: 'staff@opensupports.com',
|
||||||
password: 'staff',
|
password: 'staff',
|
||||||
@ -86,4 +87,4 @@ describe '/user/get-users' do
|
|||||||
(result['data']['users'][3]['name']).should.equal('Cersei Lannister')
|
(result['data']['users'][3]['name']).should.equal('Cersei Lannister')
|
||||||
(result['data']['users'][4]['name']).should.equal('Tyrion Lannister')
|
(result['data']['users'][4]['name']).should.equal('Tyrion Lannister')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,6 +12,7 @@ describe '/user/get' do
|
|||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token
|
csrf_token: $csrf_token
|
||||||
})
|
})
|
||||||
|
|
||||||
@ticketNumber = result['data']['ticketNumber']
|
@ticketNumber = result['data']['ticketNumber']
|
||||||
|
|
||||||
it 'should fail if not logged' do
|
it 'should fail if not logged' do
|
||||||
@ -54,4 +55,4 @@ describe '/user/get' do
|
|||||||
(ticketFromUser['owner']).should.equal(nil)
|
(ticketFromUser['owner']).should.equal(nil)
|
||||||
(ticketFromUser['events']).should.equal([])
|
(ticketFromUser['events']).should.equal([])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,6 +8,11 @@ describe '/user/signup' do
|
|||||||
|
|
||||||
userRow = $database.getRow('user', response['data']['userId'])
|
userRow = $database.getRow('user', response['data']['userId'])
|
||||||
|
|
||||||
|
request('/user/verify', {
|
||||||
|
:email => 'steve@jobs.com',
|
||||||
|
:token => userRow['verification_token']
|
||||||
|
})
|
||||||
|
|
||||||
(userRow['email']).should.equal('steve@jobs.com')
|
(userRow['email']).should.equal('steve@jobs.com')
|
||||||
(userRow['name']).should.equal('Steve Jobs')
|
(userRow['name']).should.equal('Steve Jobs')
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user