Merged in os-84-frontend-ticket-events (pull request #69)
Os 85 Frontend - Staff Ticket view
This commit is contained in:
commit
61ecc00faf
|
@ -4,7 +4,7 @@ import classNames from 'classnames';
|
|||
import i18n from 'lib-app/i18n';
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class TicketAction extends React.Component {
|
||||
class TicketEvent extends React.Component {
|
||||
static propTypes = {
|
||||
type: React.PropTypes.oneOf([
|
||||
'COMMENT',
|
||||
|
@ -31,12 +31,12 @@ class TicketAction extends React.Component {
|
|||
|
||||
return (
|
||||
<div className={this.getClass()}>
|
||||
<span className="ticket-action__connector" />
|
||||
<span className="ticket-event__connector" />
|
||||
<div className="col-md-1">
|
||||
{iconNode}
|
||||
</div>
|
||||
<div className="col-md-11">
|
||||
{this.renderActionDescription()}
|
||||
{this.renderEventDescription()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -44,21 +44,21 @@ class TicketAction extends React.Component {
|
|||
|
||||
renderStaffPic() {
|
||||
return (
|
||||
<div className="ticket-action__staff-pic">
|
||||
<img src={this.props.author.profilePic} className="ticket-action__staff-pic-img" />
|
||||
<div className="ticket-event__staff-pic">
|
||||
<img src={this.props.author.profilePic} className="ticket-event__staff-pic-img" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderIcon() {
|
||||
return (
|
||||
<div className="ticket-action__icon">
|
||||
<div className="ticket-event__icon">
|
||||
<Icon {...this.getIconProps()}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderActionDescription() {
|
||||
renderEventDescription() {
|
||||
const renders = {
|
||||
'COMMENT': this.renderComment.bind(this),
|
||||
'ASSIGN': this.renderAssignment.bind(this),
|
||||
|
@ -74,14 +74,14 @@ class TicketAction extends React.Component {
|
|||
|
||||
renderComment() {
|
||||
return (
|
||||
<div className="ticket-action__comment">
|
||||
<span className="ticket-action__comment-pointer" />
|
||||
<div className="ticket-action__comment-author">
|
||||
<span className="ticket-action__comment-author-name">{this.props.author.name}</span>
|
||||
<span className="ticket-action__comment-author-type">({i18n((this.props.author.staff) ? 'STAFF' : 'CUSTOMER')})</span>
|
||||
<div className="ticket-event__comment">
|
||||
<span className="ticket-event__comment-pointer" />
|
||||
<div className="ticket-event__comment-author">
|
||||
<span className="ticket-event__comment-author-name">{this.props.author.name}</span>
|
||||
<span className="ticket-event__comment-author-type">({i18n((this.props.author.staff) ? 'STAFF' : 'CUSTOMER')})</span>
|
||||
</div>
|
||||
<div className="ticket-action__comment-date">{this.props.date}</div>
|
||||
<div className="ticket-action__comment-content" dangerouslySetInnerHTML={{__html: this.props.content}}></div>
|
||||
<div className="ticket-event__comment-date">{this.props.date}</div>
|
||||
<div className="ticket-event__comment-content" dangerouslySetInnerHTML={{__html: this.props.content}}></div>
|
||||
{this.renderFileRow(this.props.file)}
|
||||
</div>
|
||||
);
|
||||
|
@ -89,62 +89,62 @@ class TicketAction extends React.Component {
|
|||
|
||||
renderAssignment() {
|
||||
return (
|
||||
<div className="ticket-action__circled">
|
||||
<span className="ticket-action__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-action__circled-text"> assigned this ticket</span>
|
||||
<span className="ticket-action__circled-date"> on {this.props.date}</span>
|
||||
<div className="ticket-event__circled">
|
||||
<span className="ticket-event__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-event__circled-text"> assigned this ticket</span>
|
||||
<span className="ticket-event__circled-date"> on {this.props.date}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderUnAssignment() {
|
||||
return (
|
||||
<div className="ticket-action__circled">
|
||||
<span className="ticket-action__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-action__circled-text"> unassigned this ticket</span>
|
||||
<span className="ticket-action__circled-date"> on {this.props.date}</span>
|
||||
<div className="ticket-event__circled">
|
||||
<span className="ticket-event__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-event__circled-text"> unassigned this ticket</span>
|
||||
<span className="ticket-event__circled-date"> on {this.props.date}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderClosed() {
|
||||
return (
|
||||
<div className="ticket-action__circled">
|
||||
<span className="ticket-action__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-action__circled-text"> closed this ticket</span>
|
||||
<span className="ticket-action__circled-date"> on {this.props.date}</span>
|
||||
<div className="ticket-event__circled">
|
||||
<span className="ticket-event__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-event__circled-text"> closed this ticket</span>
|
||||
<span className="ticket-event__circled-date"> on {this.props.date}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderReOpened() {
|
||||
return (
|
||||
<div className="ticket-action__circled">
|
||||
<span className="ticket-action__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-action__circled-text"> reopen this ticket</span>
|
||||
<span className="ticket-action__circled-date"> on {this.props.date}</span>
|
||||
<div className="ticket-event__circled">
|
||||
<span className="ticket-event__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-event__circled-text"> reopen this ticket</span>
|
||||
<span className="ticket-event__circled-date"> on {this.props.date}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderDepartmentChange() {
|
||||
return (
|
||||
<div className="ticket-action__circled">
|
||||
<span className="ticket-action__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-action__circled-text"> change department to</span>
|
||||
<span className="ticket-action__circled-indication"> {this.props.content}</span>
|
||||
<span className="ticket-action__circled-date"> on {this.props.date}</span>
|
||||
<div className="ticket-event__circled">
|
||||
<span className="ticket-event__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-event__circled-text"> change department to</span>
|
||||
<span className="ticket-event__circled-indication"> {this.props.content}</span>
|
||||
<span className="ticket-event__circled-date"> on {this.props.date}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderPriorityChange() {
|
||||
return (
|
||||
<div className="ticket-action__circled">
|
||||
<span className="ticket-action__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-action__circled-text"> change priority to</span>
|
||||
<span className="ticket-action__circled-indication"> {this.props.content}</span>
|
||||
<span className="ticket-action__circled-date"> on {this.props.date}</span>
|
||||
<div className="ticket-event__circled">
|
||||
<span className="ticket-event__circled-author">{this.props.author.name}</span>
|
||||
<span className="ticket-event__circled-text"> change priority to</span>
|
||||
<span className="ticket-event__circled-indication"> {this.props.content}</span>
|
||||
<span className="ticket-event__circled-date"> on {this.props.date}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -177,14 +177,14 @@ class TicketAction extends React.Component {
|
|||
};
|
||||
const classes = {
|
||||
'row': true,
|
||||
'ticket-action': true,
|
||||
'ticket-action_staff': this.props.author && this.props.author.staff,
|
||||
'ticket-action_circled': circledTypes[this.props.type],
|
||||
'ticket-action_unassignment': this.props.type === 'UN_ASSIGN',
|
||||
'ticket-action_close': this.props.type === 'CLOSE',
|
||||
'ticket-action_reopen': this.props.type === 'RE_OPEN',
|
||||
'ticket-action_department': this.props.type === 'DEPARTMENT_CHANGED',
|
||||
'ticket-action_priority': this.props.type === 'PRIORITY_CHANGED'
|
||||
'ticket-event': true,
|
||||
'ticket-event_staff': this.props.author && this.props.author.staff,
|
||||
'ticket-event_circled': circledTypes[this.props.type],
|
||||
'ticket-event_unassignment': this.props.type === 'UN_ASSIGN',
|
||||
'ticket-event_close': this.props.type === 'CLOSE',
|
||||
'ticket-event_reopen': this.props.type === 'RE_OPEN',
|
||||
'ticket-event_department': this.props.type === 'DEPARTMENT_CHANGED',
|
||||
'ticket-event_priority': this.props.type === 'PRIORITY_CHANGED'
|
||||
};
|
||||
|
||||
return classNames(classes);
|
||||
|
@ -225,4 +225,4 @@ class TicketAction extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default TicketAction;
|
||||
export default TicketEvent;
|
|
@ -1,6 +1,6 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.ticket-action {
|
||||
.ticket-event {
|
||||
margin-top: 20px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
|
@ -91,11 +91,11 @@
|
|||
}
|
||||
|
||||
&_staff {
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
background-color: $primary-blue;
|
||||
}
|
||||
|
||||
.ticket-action__comment-author-type {
|
||||
.ticket-event__comment-author-type {
|
||||
color: $primary-blue;
|
||||
}
|
||||
}
|
||||
|
@ -104,11 +104,11 @@
|
|||
margin-top: 35px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.ticket-action__connector {
|
||||
.ticket-event__connector {
|
||||
top: 28px;
|
||||
}
|
||||
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
background-color: white;
|
||||
color: $primary-black;
|
||||
border: 3px solid $light-grey;
|
||||
|
@ -121,48 +121,48 @@
|
|||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.ticket-action__circled {
|
||||
.ticket-event__circled {
|
||||
color: $primary-black;
|
||||
font-size: $font-size--sm;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.ticket-action__circled-author {
|
||||
.ticket-event__circled-author {
|
||||
color: $secondary-blue;
|
||||
}
|
||||
|
||||
.ticket-action__circled-indication {
|
||||
.ticket-event__circled-indication {
|
||||
color: $primary-green;
|
||||
}
|
||||
}
|
||||
|
||||
&_unassignment {
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&_close {
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
padding-left: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
&_reopen {
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
padding-left: 9px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&_department {
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&_priority {
|
||||
.ticket-action__icon {
|
||||
.ticket-event__icon {
|
||||
padding-left: 11px;
|
||||
padding-top: 5px;
|
||||
}
|
|
@ -4,19 +4,27 @@ import _ from 'lodash';
|
|||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
import store from 'app/store';
|
||||
import SessionStore from 'lib-app/session-store';
|
||||
import SessionActions from 'actions/session-actions';
|
||||
|
||||
import TicketAction from 'app-components/ticket-action';
|
||||
import TicketEvent from 'app-components/ticket-event';
|
||||
import AreYouSure from 'app-components/are-you-sure';
|
||||
import Form from 'core-components/form';
|
||||
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';
|
||||
|
||||
class TicketViewer extends React.Component {
|
||||
static propTypes = {
|
||||
ticket: React.PropTypes.object
|
||||
ticket: React.PropTypes.object,
|
||||
onChange: React.PropTypes.func,
|
||||
editable: React.PropTypes.bool,
|
||||
assignmentAllowed: React.PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
editable: false,
|
||||
ticket: {
|
||||
author: {},
|
||||
department: {},
|
||||
|
@ -32,7 +40,6 @@ class TicketViewer extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const ticket = this.props.ticket;
|
||||
|
||||
|
@ -42,21 +49,12 @@ class TicketViewer extends React.Component {
|
|||
<span className="ticket-viewer__number">#{ticket.ticketNumber}</span>
|
||||
<span className="ticket-viewer__title">{ticket.title}</span>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-header row">
|
||||
<div className="ticket-viewer__department col-md-4">{i18n('DEPARTMENT')}</div>
|
||||
<div className="ticket-viewer__author col-md-4">{i18n('AUTHOR')}</div>
|
||||
<div className="ticket-viewer__date col-md-4">{i18n('DATE')}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="ticket-viewer__department col-md-4">{ticket.department.name}</div>
|
||||
<div className="ticket-viewer__author col-md-4">{ticket.author.name}</div>
|
||||
<div className="ticket-viewer__date col-md-4">{ticket.date}</div>
|
||||
</div>
|
||||
{this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
|
||||
<div className="ticket-viewer__content">
|
||||
<TicketAction type="COMMENT" author={ticket.author} content={ticket.content} date={ticket.date} file={ticket.file}/>
|
||||
<TicketEvent type="COMMENT" author={ticket.author} content={ticket.content} date={ticket.date} file={ticket.file}/>
|
||||
</div>
|
||||
<div className="ticket-viewer__comments">
|
||||
{ticket.actions && ticket.actions.map(this.renderAction.bind(this))}
|
||||
{ticket.events && ticket.events.map(this.renderTicketEvent.bind(this))}
|
||||
</div>
|
||||
<div className="ticket-viewer__response">
|
||||
<div className="ticket-viewer__response-title row">{i18n('RESPOND')}</div>
|
||||
|
@ -71,12 +69,179 @@ class TicketViewer extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderAction(action, index) {
|
||||
renderEditableHeaders() {
|
||||
const ticket = this.props.ticket;
|
||||
const departments = SessionStore.getDepartments();
|
||||
const priorities = {
|
||||
'low': 0,
|
||||
'medium': 1,
|
||||
'high': 2
|
||||
};
|
||||
const priorityList = [
|
||||
{content: i18n('LOW')},
|
||||
{content: i18n('MEDIUM')},
|
||||
{content: i18n('HIGH')}
|
||||
];
|
||||
|
||||
return (
|
||||
<TicketAction {...action} key={index} />
|
||||
<div className="ticket-viewer__headers">
|
||||
<div className="ticket-viewer__info-row-header row">
|
||||
<div className="col-md-4">{i18n('DEPARTMENT')}</div>
|
||||
<div className=" col-md-4">{i18n('AUTHOR')}</div>
|
||||
<div className="col-md-4">{i18n('DATE')}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="col-md-4">
|
||||
<DropDown className="ticket-viewer__editable-dropdown"
|
||||
items={departments.map((department) => {return {content: department.name}})}
|
||||
selectedIndex={_.findIndex(departments, {id: this.props.ticket.department.id})}
|
||||
onChange={this.onDepartmentDropdownChanged.bind(this)} />
|
||||
</div>
|
||||
<div className="col-md-4">{ticket.author.name}</div>
|
||||
<div className="col-md-4">{ticket.date}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-header row">
|
||||
<div className="col-md-4">{i18n('PRIORITY')}</div>
|
||||
<div className="col-md-4">{i18n('OWNED')}</div>
|
||||
<div className="col-md-4">{i18n('STATUS')}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="col-md-4">
|
||||
<DropDown className="ticket-viewer__editable-dropdown" items={priorityList} selectedIndex={priorities[ticket.priority]} onChange={this.onPriorityDropdownChanged.bind(this)} />
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<Button type={(ticket.owner) ? 'primary' : 'secondary'} size="extra-small" onClick={this.onAssignClick.bind(this)}>
|
||||
{i18n(ticket.owner ? 'UN_ASSIGN' : 'ASSIGN_TO_ME')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<Button type={(ticket.closed) ? 'secondary' : 'primary'} size="extra-small" onClick={this.onCloseClick.bind(this)}>
|
||||
{i18n(ticket.closed ? 'RE_OPEN' : 'CLOSE')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeaders() {
|
||||
const ticket = this.props.ticket;
|
||||
const priorities = {
|
||||
'low': 'LOW',
|
||||
'medium': 'MEDIUM',
|
||||
'high': 'HIGH'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ticket-viewer__headers">
|
||||
<div className="ticket-viewer__info-row-header row">
|
||||
<div className="ticket-viewer__department col-md-4">{i18n('DEPARTMENT')}</div>
|
||||
<div className="ticket-viewer__author col-md-4">{i18n('AUTHOR')}</div>
|
||||
<div className="ticket-viewer__date col-md-4">{i18n('DATE')}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="ticket-viewer__department col-md-4">{ticket.department.name}</div>
|
||||
<div className="ticket-viewer__author col-md-4">{ticket.author.name}</div>
|
||||
<div className="ticket-viewer__date col-md-4">{ticket.date}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-header row">
|
||||
<div className="ticket-viewer__department col-md-4">{i18n('PRIORITY')}</div>
|
||||
<div className="ticket-viewer__author col-md-4">{i18n('OWNER')}</div>
|
||||
<div className="ticket-viewer__date col-md-4">{i18n('STATUS')}</div>
|
||||
</div>
|
||||
<div className="ticket-viewer__info-row-values row">
|
||||
<div className="col-md-4">
|
||||
{i18n(priorities[this.props.ticket.priority || 'low'])}
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
{(ticket.owner) ? ticket.owner.name : i18n('NONE')}
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
{this.renderOwnerNode()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOwnerNode() {
|
||||
let ownerNode = null;
|
||||
|
||||
if (this.props.assignmentAllowed && _.isEmpty(this.props.ticket.owner)) {
|
||||
ownerNode = (
|
||||
<Button type="secondary" size="extra-small" onClick={this.onAssignClick.bind(this)}>
|
||||
{i18n('ASSIGN_TO_ME')}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
ownerNode = i18n((this.props.closed) ? 'CLOSED' : 'OPENED');
|
||||
}
|
||||
|
||||
return ownerNode;
|
||||
}
|
||||
|
||||
renderTicketEvent(options, index) {
|
||||
return (
|
||||
<TicketEvent {...options} key={index} />
|
||||
);
|
||||
}
|
||||
|
||||
onDepartmentDropdownChanged(event) {
|
||||
AreYouSure.openModal(null, this.changeDepartment.bind(this, event.index));
|
||||
}
|
||||
|
||||
onPriorityDropdownChanged(event) {
|
||||
AreYouSure.openModal(null, this.changePriority.bind(this, event.index));
|
||||
}
|
||||
|
||||
onAssignClick() {
|
||||
API.call({
|
||||
path: (this.props.ticket.owner) ? '/staff/un-assign-ticket' : '/staff/assign-ticket',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this));
|
||||
}
|
||||
|
||||
onCloseClick() {
|
||||
AreYouSure.openModal(null, this.toggleClose.bind(this));
|
||||
}
|
||||
|
||||
toggleClose() {
|
||||
API.call({
|
||||
path: (this.props.ticket.closed) ? '/ticket/re-open' : '/ticket/close',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this));
|
||||
}
|
||||
|
||||
changeDepartment(index) {
|
||||
API.call({
|
||||
path: '/ticket/change-department',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber,
|
||||
departmentId: SessionStore.getDepartments()[index].id
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this));
|
||||
}
|
||||
|
||||
changePriority(index) {
|
||||
const priorities = [
|
||||
'low',
|
||||
'medium',
|
||||
'high'
|
||||
];
|
||||
|
||||
API.call({
|
||||
path: '/ticket/change-priority',
|
||||
data: {
|
||||
ticketNumber: this.props.ticket.ticketNumber,
|
||||
priority: priorities[index]
|
||||
}
|
||||
}).then(this.onTicketModification.bind(this));
|
||||
}
|
||||
|
||||
onSubmit(formState) {
|
||||
this.setState({
|
||||
loading: true
|
||||
|
@ -95,7 +260,7 @@ class TicketViewer extends React.Component {
|
|||
loading: false
|
||||
});
|
||||
|
||||
store.dispatch(SessionActions.getUserData());
|
||||
this.onTicketModification();
|
||||
}
|
||||
|
||||
onCommentFail() {
|
||||
|
@ -103,6 +268,12 @@ class TicketViewer extends React.Component {
|
|||
loading: false
|
||||
});
|
||||
}
|
||||
|
||||
onTicketModification() {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TicketViewer;
|
|
@ -28,18 +28,15 @@
|
|||
&__info-row-values {
|
||||
background-color: $light-grey;
|
||||
color: $secondary-blue;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
&__date {
|
||||
&__editable-dropdown {
|
||||
margin: auto;
|
||||
|
||||
.drop-down__current-item {
|
||||
background-color: $very-light-grey;
|
||||
}
|
||||
|
||||
&__author {
|
||||
|
||||
}
|
||||
|
||||
&__department {
|
||||
|
||||
}
|
||||
|
||||
&__content {
|
||||
|
|
|
@ -84,7 +84,7 @@ export default (
|
|||
<Route path="new-tickets" component={AdminPanelNewTickets} />
|
||||
<Route path="all-tickets" component={AdminPanelAllTickets} />
|
||||
<Route path="custom-responses" component={AdminPanelCustomResponses} />
|
||||
<Route path="view-ticket" component={AdminPanelViewTicket} />
|
||||
<Route path="view-ticket/:ticketNumber" component={AdminPanelViewTicket} />
|
||||
</Route>
|
||||
|
||||
<Route path="users">
|
||||
|
|
|
@ -1,14 +1,98 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import API from 'lib-app/api-call';
|
||||
import i18n from 'lib-app/i18n';
|
||||
import SessionStore from 'lib-app/session-store';
|
||||
|
||||
import TicketViewer from 'app-components/ticket-viewer';
|
||||
import Loading from 'core-components/loading';
|
||||
import Header from 'core-components/header';
|
||||
|
||||
class AdminPanelViewTicket extends React.Component {
|
||||
|
||||
state = {
|
||||
loading: true,
|
||||
ticket: {}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.retrieveTicket();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
/admin/panel/tickets/view-ticket
|
||||
<div className="admin-panel-view-ticket">
|
||||
<Header title={i18n('VIEW_TICKET')} description={i18n('TICKET_VIEW_DESCRIPTION')} />
|
||||
{(this.state.loading) ? this.renderLoading() : this.renderView()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderLoading() {
|
||||
return (
|
||||
<div className="admin-panel-view-ticket__loading">
|
||||
<Loading size="large" />
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
renderView() {
|
||||
return (_.isEmpty(this.state.ticket)) ? this.renderNoPermissionError() : this.renderTicketView();
|
||||
}
|
||||
|
||||
renderNoPermissionError() {
|
||||
return (
|
||||
<div className="admin-panel-view-ticket__error">
|
||||
{i18n('NO_PERMISSION')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTicketView() {
|
||||
return (
|
||||
<div className="admin-panel-view-ticket__ticket-view">
|
||||
<TicketViewer {...this.getTicketViewProps()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getTicketViewProps() {
|
||||
return {
|
||||
ticket: this.state.ticket,
|
||||
onChange: this.retrieveTicket.bind(this),
|
||||
assignmentAllowed: true,
|
||||
editable: _.get(this.state.ticket, 'owner.id') === SessionStore.getUserData().id
|
||||
};
|
||||
}
|
||||
|
||||
retrieveTicket() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
ticket: {}
|
||||
});
|
||||
|
||||
API.call({
|
||||
path: '/ticket/get',
|
||||
date: {
|
||||
ticketNumber: this.props.params.ticketNumber
|
||||
}
|
||||
}).then(this.onRetrieveSuccess.bind(this)).catch(this.onRetrieveFail.bind(this))
|
||||
}
|
||||
|
||||
onRetrieveSuccess(result) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
ticket: result.data
|
||||
});
|
||||
}
|
||||
|
||||
onRetrieveFail() {
|
||||
this.setState({
|
||||
loading: false,
|
||||
ticket: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminPanelViewTicket;
|
|
@ -0,0 +1,18 @@
|
|||
@import '../../../../scss/vars';
|
||||
|
||||
.admin-panel-view-ticket {
|
||||
margin: 10px;
|
||||
|
||||
&__loading {
|
||||
background-color: $grey;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
&__error {
|
||||
|
||||
}
|
||||
|
||||
&__ticket-view {
|
||||
margin: 20px 30px;
|
||||
}
|
||||
}
|
|
@ -42,8 +42,8 @@ class CreateTicketForm extends React.Component {
|
|||
{(!this.props.userLogged) ? this.renderEmailAndName() : null}
|
||||
<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="departmentId" field="select" fieldProps={{
|
||||
items: SessionStore.getDepartments().map((department) => {return {content: department}}),
|
||||
<FormField className="col-md-5" label="Department" name="departmentIndex" field="select" fieldProps={{
|
||||
items: SessionStore.getDepartments().map((department) => {return {content: department.name}}),
|
||||
size: 'medium'
|
||||
}} />
|
||||
</div>
|
||||
|
@ -92,7 +92,7 @@ class CreateTicketForm extends React.Component {
|
|||
API.call({
|
||||
path: '/ticket/create',
|
||||
data: _.extend({}, formState, {
|
||||
departmentId: formState.departmentId + 1
|
||||
departmentId: SessionStore.getDepartments()[formState.departmentIndex].id
|
||||
})
|
||||
}).then(this.onTicketSuccess.bind(this)).catch(this.onTicketFail.bind(this));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import _ from 'lodash';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import SessionActions from 'actions/session-actions';
|
||||
import TicketViewer from 'app-components/ticket-viewer';
|
||||
|
||||
class DashboardTicketPage extends React.Component {
|
||||
|
@ -13,7 +14,7 @@ class DashboardTicketPage extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="dashboard-ticket-page">
|
||||
<TicketViewer ticket={this.getTicketData()} />
|
||||
<TicketViewer ticket={this.getTicketData()} onChange={this.retrieveUserData.bind(this)}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -21,6 +22,10 @@ class DashboardTicketPage extends React.Component {
|
|||
getTicketData() {
|
||||
return _.find(this.props.tickets, {ticketNumber: this.props.params.ticketNumber});
|
||||
}
|
||||
|
||||
retrieveUserData() {
|
||||
this.props.dispatch(SessionActions.getUserData());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect((store) => {
|
||||
|
|
|
@ -18,6 +18,7 @@ class Button extends React.Component {
|
|||
static propTypes = {
|
||||
children: React.PropTypes.node,
|
||||
size: React.PropTypes.oneOf([
|
||||
'extra-small',
|
||||
'small',
|
||||
'medium',
|
||||
'large',
|
||||
|
@ -76,6 +77,7 @@ class Button extends React.Component {
|
|||
'button_clean': (this.props.type === 'clean'),
|
||||
'button_link': (this.props.type === 'link'),
|
||||
|
||||
'button_extra-small': (this.props.size === 'extra-small'),
|
||||
'button_small': (this.props.size === 'small'),
|
||||
'button_medium': (this.props.size === 'medium'),
|
||||
'button_large': (this.props.size === 'large'),
|
||||
|
|
|
@ -47,6 +47,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
&_extra-small {
|
||||
text-transform: none;
|
||||
width: 130px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&_small {
|
||||
width: 100px;
|
||||
height: 47px;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
&__list-container {
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
z-index: 5;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&_closed {
|
||||
|
|
|
@ -9,9 +9,9 @@ module.exports = [
|
|||
'language': 'en',
|
||||
'reCaptchaKey': '6LfM5CYTAAAAAGLz6ctpf-hchX2_l0Ge-Bn-n8wS',
|
||||
'departments': [
|
||||
'Sales Support',
|
||||
'Technical Issues',
|
||||
'System and Administration'
|
||||
{id: 1, name: 'Sales Support'},
|
||||
{id: 2, name: 'Technical Issues'},
|
||||
{id: 3, name: 'System and Administration'}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
@ -77,5 +77,130 @@ module.exports = [
|
|||
data: {}
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/ticket/get',
|
||||
time: 1000,
|
||||
response: function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: {
|
||||
ticketNumber: '878552',
|
||||
title: 'Lorem ipsum door',
|
||||
content: 'I had a problem with the installation of the php server',
|
||||
department: {
|
||||
id: 1,
|
||||
name: 'Sales Support'
|
||||
},
|
||||
date: '20160415',
|
||||
file: 'http://www.opensupports.com/some_file.zip',
|
||||
language: 'en',
|
||||
unread: false,
|
||||
closed: false,
|
||||
priority: 'medium',
|
||||
author: {
|
||||
name: 'Haskell Curry',
|
||||
email: 'haskell@lambda.com'
|
||||
},
|
||||
owner: {
|
||||
name: 'Steve Jobs'
|
||||
},
|
||||
events: [
|
||||
{
|
||||
type: 'ASSIGN',
|
||||
date: '20150409',
|
||||
author: {
|
||||
name: 'Emilia Clarke',
|
||||
email: 'jobs@steve.com',
|
||||
profilePic: 'http://i65.tinypic.com/9bep95.jpg',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'COMMENT',
|
||||
date: '20150409',
|
||||
content: 'Do you have apache installed? It generally happens if you dont have apache.',
|
||||
author: {
|
||||
name: 'Emilia Clarke',
|
||||
email: 'jobs@steve.com',
|
||||
profilePic: 'http://i65.tinypic.com/9bep95.jpg',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'UN_ASSIGN',
|
||||
date: '20150410',
|
||||
author: {
|
||||
name: 'Emilia Clarke',
|
||||
email: 'jobs@steve.com',
|
||||
profilePic: 'http://i65.tinypic.com/9bep95.jpg',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'DEPARTMENT_CHANGED',
|
||||
date: '20150411',
|
||||
content: 'System support',
|
||||
author: {
|
||||
name: 'Emilia Clarke',
|
||||
email: 'jobs@steve.com',
|
||||
profilePic: 'http://i65.tinypic.com/9bep95.jpg',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'COMMENT',
|
||||
date: '20150412',
|
||||
content: 'I have already installed apache, but the problem persists',
|
||||
author: {
|
||||
name: 'Haskell Curry',
|
||||
steve: 'haskell@lambda.com',
|
||||
staff: false
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'PRIORITY_CHANGED',
|
||||
date: '20150413',
|
||||
content: 'MEDIUM',
|
||||
author: {
|
||||
name: 'Emilia Clarke',
|
||||
email: 'jobs@steve.com',
|
||||
profilePic: 'http://i65.tinypic.com/9bep95.jpg',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'COMMENT',
|
||||
date: '20150511',
|
||||
content: 'Thanks!, I soved it by myself',
|
||||
author: {
|
||||
name: 'Haskell Curry',
|
||||
steve: 'haskell@lambda.com',
|
||||
staff: false
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'CLOSE',
|
||||
date: '20150513',
|
||||
author: {
|
||||
name: 'Emilia Clarke',
|
||||
email: 'jobs@steve.com',
|
||||
profilePic: 'http://i65.tinypic.com/9bep95.jpg',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'RE_OPEN',
|
||||
date: '20151018',
|
||||
author: {
|
||||
name: 'Haskell Curry',
|
||||
email: 'haskell@lambda.com',
|
||||
staff: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
|
@ -43,7 +43,7 @@ module.exports = [
|
|||
return {
|
||||
status: 'success',
|
||||
data: {
|
||||
sessionActive: false
|
||||
sessionActive: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ module.exports = [
|
|||
content: 'I had a problem with the installation of the php server',
|
||||
department: {
|
||||
id: 2,
|
||||
name: 'Environment Setup'
|
||||
name: 'Technical Issues'
|
||||
},
|
||||
date: '20160416',
|
||||
file: 'http://www.opensupports.com/some_file.zip',
|
||||
|
@ -158,7 +158,7 @@ module.exports = [
|
|||
name: 'Steve Jobs',
|
||||
email: 'steve@jobs.com'
|
||||
},
|
||||
actions: [
|
||||
events: [
|
||||
{
|
||||
type: 'ASSIGN',
|
||||
date: '20150409',
|
||||
|
@ -258,8 +258,8 @@ module.exports = [
|
|||
title: 'Lorem ipsum door',
|
||||
content: 'I had a problem with the installation of the php server',
|
||||
department: {
|
||||
id: 2,
|
||||
name: 'Environment Setup'
|
||||
id: 1,
|
||||
name: 'Sales Support'
|
||||
},
|
||||
date: '20160415',
|
||||
file: 'http://www.opensupports.com/some_file.zip',
|
||||
|
@ -274,7 +274,7 @@ module.exports = [
|
|||
owner: {
|
||||
name: 'Steve Jobs'
|
||||
},
|
||||
actions: [
|
||||
events: [
|
||||
{
|
||||
type: 'ASSIGN',
|
||||
date: '20150409',
|
||||
|
@ -374,8 +374,8 @@ module.exports = [
|
|||
title: 'Lorem ipsum door',
|
||||
content: 'I had a problem with the installation of the php server',
|
||||
department: {
|
||||
id: 2,
|
||||
name: 'Environment Setup'
|
||||
id: 1,
|
||||
name: 'Sales Support'
|
||||
},
|
||||
date: '20150409',
|
||||
file: 'http://www.opensupports.com/some_file.zip',
|
||||
|
@ -390,7 +390,7 @@ module.exports = [
|
|||
owner: {
|
||||
name: 'Steve Jobs'
|
||||
},
|
||||
actions: [
|
||||
events: [
|
||||
{
|
||||
type: 'ASSIGN',
|
||||
date: '20150409',
|
||||
|
|
|
@ -60,6 +60,17 @@ export default {
|
|||
'DISCARD_CHANGES': 'Discard changes',
|
||||
'DELETE': 'Delete',
|
||||
'LANGUAGE': 'Language',
|
||||
'OWNER': 'Owner',
|
||||
'OWNED': 'Owned',
|
||||
'STATUS': 'Status',
|
||||
'NONE': 'None',
|
||||
'OPENED': 'Opened',
|
||||
'CLOSED': 'Closed',
|
||||
'CLOSE': 'Close',
|
||||
'RE_OPEN': 'Re open',
|
||||
'ASSIGN_TO_ME': 'Assign to me',
|
||||
'UN_ASSIGN': 'Unassign',
|
||||
'VIEW_TICKET': 'View Ticket',
|
||||
|
||||
//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.',
|
||||
|
@ -69,6 +80,7 @@ export default {
|
|||
'ACCOUNT_DESCRIPTION': 'All your tickets are stored in your accounts\'s profile. Keep track off all your tickets you send to our staff team.',
|
||||
'SUPPORT_CENTER_DESCRIPTION': 'Welcome to our support center. You can contact us through a tickets system. Your tickets will be answered by our staff.',
|
||||
'CUSTOM_RESPONSES_DESCRIPTION': 'Custom responses are automated responses for common problems',
|
||||
'TICKET_VIEW_DESCRIPTION': 'This ticket has been sent by a customer. Here you can respond or assign the ticket',
|
||||
|
||||
//ERRORS
|
||||
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
||||
|
@ -82,6 +94,7 @@ export default {
|
|||
'PASSWORD_NOT_MATCH': 'Password does not match',
|
||||
'INVALID_RECOVER': 'Invalid recover data',
|
||||
'TICKET_SENT_ERROR': 'An error occurred while trying to create the ticket.',
|
||||
'NO_PERMISSION': 'You\'ve no permission to access to this page.',
|
||||
|
||||
//MESSAGES
|
||||
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',
|
||||
|
|
Loading…
Reference in New Issue