mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-31 01:35:15 +02:00
Ivan - Frontend - Create ticket viewer implementation with comments [skip ci]
This commit is contained in:
parent
3a9d009fff
commit
d5514fe62e
105
client/src/app/main/dashboard/dashboard-ticket/ticket-action.js
Normal file
105
client/src/app/main/dashboard/dashboard-ticket/ticket-action.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import Icon from 'core-components/icon';
|
||||||
|
|
||||||
|
class TicketAction extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
type: React.PropTypes.oneOf(['comment', 'assign']),
|
||||||
|
config: React.PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
type: 'comment'
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={this.getClass()}>
|
||||||
|
<span className="ticket-action__connector" />
|
||||||
|
<div className="col-md-1">
|
||||||
|
<div className="ticket-action__icon">
|
||||||
|
<Icon name="comment-o" size="2x" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-11">
|
||||||
|
{this.renderActionDescription()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderActionDescription() {
|
||||||
|
const renders = {
|
||||||
|
'comment': this.renderComment.bind(this),
|
||||||
|
'assign': this.renderAssignment.bind(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
return renders[this.props.type]();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderComment() {
|
||||||
|
const {config} = this.props;
|
||||||
|
|
||||||
|
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">{config.author.name}</span>
|
||||||
|
<span className="ticket-action__comment-author-type">({i18n((config.author.staff) ? 'STAFF' : 'CUSTOMER')})</span>
|
||||||
|
</div>
|
||||||
|
<div className="ticket-action__comment-date">{config.date}</div>
|
||||||
|
<div className="ticket-action__comment-content">{config.content}</div>
|
||||||
|
{this.renderFileRow(config.file)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAssignment() {
|
||||||
|
// TODO: Add actions architecture instead of just comments
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ticket-action__assignment">
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFileRow(file) {
|
||||||
|
let node = null;
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
node = <span> {this.getFileLink(file)} <Icon name="paperclip" /> </span>;
|
||||||
|
} else {
|
||||||
|
node = i18n('NO_ATTACHMENT');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ticket-viewer__file">
|
||||||
|
{node}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getClass() {
|
||||||
|
const {config} = this.props;
|
||||||
|
|
||||||
|
let classes = {
|
||||||
|
'row': true,
|
||||||
|
'ticket-action': true,
|
||||||
|
'ticket-action_staff': config.author && config.author.staff
|
||||||
|
};
|
||||||
|
|
||||||
|
return classNames(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileLink(filePath = '') {
|
||||||
|
const fileName = filePath.replace(/^.*[\\\/]/, '');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a href={filePath} target="_blank">{fileName}</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TicketAction;
|
@ -0,0 +1,83 @@
|
|||||||
|
@import "../../../../scss/vars";
|
||||||
|
|
||||||
|
.ticket-action {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&__connector {
|
||||||
|
position: absolute;
|
||||||
|
background-color: $light-grey;
|
||||||
|
width: 3px;
|
||||||
|
height: 100%;
|
||||||
|
top: 38px;
|
||||||
|
left: 33px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
vertical-align: top;
|
||||||
|
background-color: $secondary-blue;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__comment {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-pointer {
|
||||||
|
right: 100%;
|
||||||
|
border: solid transparent;
|
||||||
|
position: absolute;
|
||||||
|
border-right-color: $light-grey;
|
||||||
|
border-width: 13px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-author {
|
||||||
|
text-align: left;
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
padding: 12px;
|
||||||
|
color: $primary-black;
|
||||||
|
|
||||||
|
&-type {
|
||||||
|
font-size: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: $secondary-blue;
|
||||||
|
font-variant: small-caps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-date {
|
||||||
|
text-align: right;
|
||||||
|
border: 2px solid $light-grey;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: $light-grey;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
background-color: white;
|
||||||
|
border: 2px solid $very-light-grey;
|
||||||
|
border-top: none;
|
||||||
|
padding: 20px 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_staff {
|
||||||
|
.ticket-action__icon {
|
||||||
|
background-color: $primary-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ticket-action__comment-author-type {
|
||||||
|
color: $primary-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import Icon from 'core-components/icon';
|
import i18n from 'lib-app/i18n';
|
||||||
|
import TicketAction from 'app/main/dashboard/dashboard-ticket/ticket-action';
|
||||||
|
import Form from 'core-components/form';
|
||||||
|
import FormField from 'core-components/form-field';
|
||||||
|
import SubmitButton from 'core-components/submit-button';
|
||||||
|
|
||||||
class TicketViewer extends React.Component {
|
class TicketViewer extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -24,9 +27,9 @@ class TicketViewer extends React.Component {
|
|||||||
<span className="ticket-viewer__title">{this.props.ticket.title}</span>
|
<span className="ticket-viewer__title">{this.props.ticket.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__info-row-header row">
|
<div className="ticket-viewer__info-row-header row">
|
||||||
<div className="ticket-viewer__department col-md-4">Department</div>
|
<div className="ticket-viewer__department col-md-4">{i18n('DEPARTMENT')}</div>
|
||||||
<div className="ticket-viewer__author col-md-4">Author</div>
|
<div className="ticket-viewer__author col-md-4">{i18n('AUTHOR')}</div>
|
||||||
<div className="ticket-viewer__date col-md-4">Date</div>
|
<div className="ticket-viewer__date col-md-4">{i18n('DATE')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__info-row-values row">
|
<div className="ticket-viewer__info-row-values row">
|
||||||
<div className="ticket-viewer__department col-md-4">{this.props.ticket.department.name}</div>
|
<div className="ticket-viewer__department col-md-4">{this.props.ticket.department.name}</div>
|
||||||
@ -34,83 +37,29 @@ class TicketViewer extends React.Component {
|
|||||||
<div className="ticket-viewer__date col-md-4">{this.props.ticket.date}</div>
|
<div className="ticket-viewer__date col-md-4">{this.props.ticket.date}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__content">
|
<div className="ticket-viewer__content">
|
||||||
{this.renderComment(this.props.ticket)}
|
<TicketAction type="comment" config={this.props.ticket} />
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__comments">
|
<div className="ticket-viewer__comments">
|
||||||
{this.props.ticket.comments.map(this.renderComment.bind(this))}
|
{this.props.ticket.comments.map(this.renderComment.bind(this))}
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__response">
|
<div className="ticket-viewer__response">
|
||||||
<div className="ticket-viewer__response-title row">Respond</div>
|
<div className="ticket-viewer__response-title row">{i18n('RESPOND')}</div>
|
||||||
<div className="ticket-viewer__response-field row">Response field</div>
|
<div className="ticket-viewer__response-field row">
|
||||||
</div>
|
<Form>
|
||||||
</div>
|
<FormField name="content" validation="TEXT_AREA" required field="textarea" />
|
||||||
);
|
<SubmitButton>{i18n('RESPOND_TICKET')}</SubmitButton>
|
||||||
}
|
</Form>
|
||||||
|
|
||||||
renderComment(comment) {
|
|
||||||
const iconNode = (
|
|
||||||
<div className="col-md-1">
|
|
||||||
<div className="ticket-viewer__comment-action">
|
|
||||||
<Icon name="comment-o" size="2x" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
const commentNode = (
|
|
||||||
<div className="col-md-11">
|
|
||||||
<div className="ticket-viewer__comment">
|
|
||||||
<span className="ticket-viewer__comment-pointer" />
|
|
||||||
<div className="ticket-viewer__comment-author">
|
|
||||||
<span className="ticket-viewer__comment-author-name">
|
|
||||||
{comment.author.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__comment-date">{comment.date}</div>
|
|
||||||
<div className="ticket-viewer__comment-content">{comment.content}</div>
|
|
||||||
{this.renderFileRow(comment.file)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderComment(comment, index) {
|
||||||
return (
|
return (
|
||||||
<div className={this.getActionClass(comment)}>
|
<TicketAction type="comment" config={comment} key={index} />
|
||||||
{comment.author.staff ? [commentNode, iconNode] : [iconNode, commentNode]}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFileRow(file) {
|
|
||||||
let node = null;
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
node = <span> {this.getFileLink(this.props.ticket.file)} <Icon name="paperclip" /> </span>;
|
|
||||||
} else {
|
|
||||||
node = 'No file attachment';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="ticket-viewer__file">
|
|
||||||
{node}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
getActionClass(action) {
|
|
||||||
let classes = {
|
|
||||||
'row': true,
|
|
||||||
'ticket-viewer__action': true,
|
|
||||||
'ticket-viewer__action_staff': action.author.staff
|
|
||||||
};
|
|
||||||
|
|
||||||
return classNames(classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFileLink(filePath = '') {
|
|
||||||
const fileName = filePath.replace(/^.*[\\\/]/, '');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<a href={filePath} target="_blank">{fileName}</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TicketViewer;
|
export default TicketViewer;
|
@ -53,75 +53,26 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__comments-title {
|
&__comments {
|
||||||
text-align: left;
|
position: relative;
|
||||||
background-color: $dark-grey;
|
|
||||||
color: white;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__action {
|
&__response {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
|
|
||||||
&_staff {
|
|
||||||
.ticket-viewer__comment-action {
|
|
||||||
background-color: $primary-blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ticket-viewer__comment-pointer {
|
|
||||||
left: 100%;
|
|
||||||
border-right-color: transparent;
|
|
||||||
border-left-color: $light-grey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__comment {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&-action {
|
&-title {
|
||||||
vertical-align: top;
|
background-color: $primary-blue;
|
||||||
background-color: $secondary-blue;
|
|
||||||
color: white;
|
|
||||||
border-radius: 5px;
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
padding-left: 8px;
|
|
||||||
padding-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-pointer {
|
|
||||||
right: 100%;
|
|
||||||
border: solid transparent;
|
|
||||||
position: absolute;
|
|
||||||
border-right-color: $light-grey;
|
|
||||||
border-width: 13px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-author {
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
float: left;
|
padding: 5px;
|
||||||
position: relative;
|
color: white;
|
||||||
padding: 12px;
|
border-top-right-radius: 4px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-date {
|
&-field {
|
||||||
text-align: right;
|
background-color: $very-light-grey;
|
||||||
border: 2px solid $light-grey;
|
padding: 20px;
|
||||||
border-bottom: none;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: $light-grey;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
&-content {
|
|
||||||
background-color: white;
|
|
||||||
border: 2px solid $very-light-grey;
|
|
||||||
border-top: none;
|
|
||||||
padding: 20px 10px;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
.text-editor {
|
.text-editor {
|
||||||
|
|
||||||
&__editor {
|
&__editor {
|
||||||
|
background-color: white;
|
||||||
border: 1px solid $grey;
|
border: 1px solid $grey;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
@ -16,6 +16,14 @@ export default {
|
|||||||
'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.',
|
||||||
'TICKET_LIST': 'Ticket List',
|
'TICKET_LIST': 'Ticket List',
|
||||||
'TICKET_LIST_DESCRIPTION': 'Here you can find a list of all tickets you have sent to our support team.',
|
'TICKET_LIST_DESCRIPTION': 'Here you can find a list of all tickets you have sent to our support team.',
|
||||||
|
'DEPARTMENT': 'Department',
|
||||||
|
'AUTHOR': 'Author',
|
||||||
|
'DATE': 'Date',
|
||||||
|
'RESPOND': 'Respond',
|
||||||
|
'RESPOND_TICKET': 'Respond Ticket',
|
||||||
|
'NO_ATTACHMENT': 'No file attachment',
|
||||||
|
'STAFF': 'Staff',
|
||||||
|
'CUSTOMER': 'Customer',
|
||||||
|
|
||||||
//ERRORS
|
//ERRORS
|
||||||
'EMAIL_NOT_EXIST': 'Email does not exist',
|
'EMAIL_NOT_EXIST': 'Email does not exist',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user