display tags in frontend
This commit is contained in:
parent
fad0e8eafb
commit
5bf8ff94bc
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import {connect} from "react-redux";
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
import DateTransformer from 'lib-core/date-transformer';
|
import DateTransformer from 'lib-core/date-transformer';
|
||||||
|
@ -11,6 +12,7 @@ import Button from 'core-components/button';
|
||||||
import Tooltip from 'core-components/tooltip';
|
import Tooltip from 'core-components/tooltip';
|
||||||
import Icon from 'core-components/icon';
|
import Icon from 'core-components/icon';
|
||||||
import Checkbox from 'core-components/checkbox';
|
import Checkbox from 'core-components/checkbox';
|
||||||
|
import Tag from 'core-components/tag';
|
||||||
|
|
||||||
class TicketList extends React.Component {
|
class TicketList extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -182,9 +184,16 @@ class TicketList extends React.Component {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
title: (
|
title: (
|
||||||
<Button className="ticket-list__title-link" type="clean" route={{to: this.props.ticketPath + ticket.ticketNumber}}>
|
<div>
|
||||||
{titleText}
|
<Button className="ticket-list__title-link" type="clean" route={{to: this.props.ticketPath + ticket.ticketNumber}}>
|
||||||
</Button>
|
{titleText}
|
||||||
|
</Button>
|
||||||
|
{ticket.tags.map((tagName,index) => {
|
||||||
|
let tag = _.find(this.props.tags, {name:tagName});
|
||||||
|
return <Tag size='small' name={tag && tag.name} color={tag && tag.color} key={index} />
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
),
|
),
|
||||||
priority: this.getTicketPriority(ticket.priority),
|
priority: this.getTicketPriority(ticket.priority),
|
||||||
department: ticket.department.name,
|
department: ticket.department.name,
|
||||||
|
@ -257,5 +266,8 @@ class TicketList extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect((store) => {
|
||||||
export default TicketList;
|
return {
|
||||||
|
tags: store.config['tags']
|
||||||
|
};
|
||||||
|
})(TicketList);
|
|
@ -22,6 +22,8 @@ import Icon from 'core-components/icon';
|
||||||
import TextEditor from 'core-components/text-editor';
|
import TextEditor from 'core-components/text-editor';
|
||||||
import InfoTooltip from 'core-components/info-tooltip';
|
import InfoTooltip from 'core-components/info-tooltip';
|
||||||
import DepartmentDropdown from 'app-components/department-dropdown';
|
import DepartmentDropdown from 'app-components/department-dropdown';
|
||||||
|
import TagSelector from 'core-components/tag-selector';
|
||||||
|
import Tag from 'core-components/tag';
|
||||||
|
|
||||||
class TicketViewer extends React.Component {
|
class TicketViewer extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -36,10 +38,12 @@ class TicketViewer extends React.Component {
|
||||||
userId: React.PropTypes.number,
|
userId: React.PropTypes.number,
|
||||||
userStaff: React.PropTypes.bool,
|
userStaff: React.PropTypes.bool,
|
||||||
userDepartments: React.PropTypes.array,
|
userDepartments: React.PropTypes.array,
|
||||||
userLevel: React.PropTypes.number
|
userLevel: React.PropTypes.number,
|
||||||
|
tags: React.PropTypes.array
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
tags: [],
|
||||||
editable: false,
|
editable: false,
|
||||||
ticket: {
|
ticket: {
|
||||||
author: {},
|
author: {},
|
||||||
|
@ -63,6 +67,7 @@ class TicketViewer extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const ticket = this.props.ticket;
|
const ticket = this.props.ticket;
|
||||||
|
console.log('tickett',ticket)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ticket-viewer">
|
<div className="ticket-viewer">
|
||||||
|
@ -105,7 +110,7 @@ class TicketViewer extends React.Component {
|
||||||
<div className="ticket-viewer__info-row-header row">
|
<div className="ticket-viewer__info-row-header row">
|
||||||
<div className="col-md-4">{i18n('DEPARTMENT')}</div>
|
<div className="col-md-4">{i18n('DEPARTMENT')}</div>
|
||||||
<div className="col-md-4">{i18n('AUTHOR')}</div>
|
<div className="col-md-4">{i18n('AUTHOR')}</div>
|
||||||
<div className="col-md-4">{i18n('DATE')}</div>
|
<div className="col-md-4">{i18n('TAGS')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__info-row-values row">
|
<div className="ticket-viewer__info-row-values row">
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
|
@ -115,7 +120,7 @@ class TicketViewer extends React.Component {
|
||||||
onChange={this.onDepartmentDropdownChanged.bind(this)} />
|
onChange={this.onDepartmentDropdownChanged.bind(this)} />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-4">{ticket.author.name}</div>
|
<div className="col-md-4">{ticket.author.name}</div>
|
||||||
<div className="col-md-4">{DateTransformer.transformToString(ticket.date)}</div>
|
<div className="col-md-4"> <TagSelector items={this.props.tags} values={this.props.ticket.tags} onRemoveClick={this.removeTag.bind(this)} onTagSelected={this.addTag.bind(this)}/></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="ticket-viewer__info-row-header row">
|
<div className="ticket-viewer__info-row-header row">
|
||||||
<div className="col-md-4">{i18n('PRIORITY')}</div>
|
<div className="col-md-4">{i18n('PRIORITY')}</div>
|
||||||
|
@ -153,12 +158,15 @@ class TicketViewer extends React.Component {
|
||||||
<div className="ticket-viewer__info-row-header row">
|
<div className="ticket-viewer__info-row-header row">
|
||||||
<div className="ticket-viewer__department col-md-4">{i18n('DEPARTMENT')}</div>
|
<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__author col-md-4">{i18n('AUTHOR')}</div>
|
||||||
<div className="ticket-viewer__date col-md-4">{i18n('DATE')}</div>
|
<div className="ticket-viewer__date col-md-4">{i18n('TAGS')}</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">{ticket.department.name}</div>
|
<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__author col-md-4">{ticket.author.name}</div>
|
||||||
<div className="ticket-viewer__date col-md-4">{DateTransformer.transformToString(ticket.date, false)}</div>
|
<div className="col-md-4">{ticket.tags.length ? ticket.tags.map((tagName,index) => {
|
||||||
|
let tag = _.find(this.props.tags, {name:tagName});
|
||||||
|
return <Tag name={tag && tag.name} color={tag && tag.color} key={index} />
|
||||||
|
}) : i18n('NONE')}</div>
|
||||||
</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">{i18n('PRIORITY')}</div>
|
<div className="ticket-viewer__department col-md-4">{i18n('PRIORITY')}</div>
|
||||||
|
@ -412,7 +420,25 @@ class TicketViewer extends React.Component {
|
||||||
}
|
}
|
||||||
}).then(this.onTicketModification.bind(this));
|
}).then(this.onTicketModification.bind(this));
|
||||||
}
|
}
|
||||||
|
addTag(tag) {
|
||||||
|
API.call({
|
||||||
|
path: '/ticket/add-tag',
|
||||||
|
data: {
|
||||||
|
ticketNumber: this.props.ticket.ticketNumber,
|
||||||
|
tagId: tag
|
||||||
|
}
|
||||||
|
}).then(this.onTicketModification.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTag(tag) {
|
||||||
|
API.call({
|
||||||
|
path: '/ticket/remove-tag',
|
||||||
|
data: {
|
||||||
|
ticketNumber: this.props.ticket.ticketNumber,
|
||||||
|
tagId: tag
|
||||||
|
}
|
||||||
|
}).then(this.onTicketModification.bind(this))
|
||||||
|
}
|
||||||
onCustomResponsesChanged({index}) {
|
onCustomResponsesChanged({index}) {
|
||||||
let replaceContentWithCustomResponse = () => {
|
let replaceContentWithCustomResponse = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -515,6 +541,7 @@ export default connect((store) => {
|
||||||
staffMembersLoaded: store.adminData.staffMembersLoaded,
|
staffMembersLoaded: store.adminData.staffMembersLoaded,
|
||||||
allowAttachments: store.config['allow-attachments'],
|
allowAttachments: store.config['allow-attachments'],
|
||||||
userSystemEnabled: store.config['user-system-enabled'],
|
userSystemEnabled: store.config['user-system-enabled'],
|
||||||
userLevel: store.session.userLevel
|
userLevel: store.session.userLevel,
|
||||||
|
tags: store.config['tags']
|
||||||
};
|
};
|
||||||
})(TicketViewer);
|
})(TicketViewer);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Icon from 'core-components/icon';
|
import Icon from 'core-components/icon';
|
||||||
import DropDown from 'core-components/drop-down';
|
import DropDown from 'core-components/drop-down';
|
||||||
|
import Tag from 'core-components/tag';
|
||||||
|
|
||||||
class TagSelector extends React.Component {
|
class TagSelector extends React.Component {
|
||||||
|
|
||||||
|
@ -32,14 +33,8 @@ class TagSelector extends React.Component {
|
||||||
|
|
||||||
|
|
||||||
renderSelectedTag(item,index) {
|
renderSelectedTag(item,index) {
|
||||||
return (
|
return <Tag name={item.name} color={item.color} showDeleteButton onRemoveClick={this.onRemoveClick.bind(this,item.id)} key={index}/>;
|
||||||
<div className="tag-selector__selected-tag" style={{backgroundColor:item.color}} onClick={event => event.stopPropagation()} key={index}>
|
|
||||||
<span className="tag-selector__selected-tag-name">{item.name}</span>
|
|
||||||
<span onClick={this.onRemoveClick.bind(this,item.name)} className="tag-selector__selected-tag-remove" >
|
|
||||||
<Icon name="times-circle" size="small"/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTagOptions() {
|
renderTagOptions() {
|
||||||
|
@ -50,24 +45,23 @@ class TagSelector extends React.Component {
|
||||||
|
|
||||||
renderTagOption(item,index) {
|
renderTagOption(item,index) {
|
||||||
return (
|
return (
|
||||||
<div onClick={this.onTagSelected.bind(this,item.name)} className="tag-selector__tag-option" key={index}>
|
<div onClick={this.onTagSelected.bind(this,item.id)} className="tag-selector__tag-option" key={index}>
|
||||||
<span className="tag-selector__tag-option-square" style={{backgroundColor:item.color}}/>
|
<span className="tag-selector__tag-option-square" style={{backgroundColor:item.color}}/>
|
||||||
<span className="tag-selector__tag-option-name" >{item.name}</span>
|
<span className="tag-selector__tag-option-name" >{item.name}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoveClick(tag) {
|
onRemoveClick(tagId) {
|
||||||
|
|
||||||
if(this.props.onRemoveClick){
|
if(this.props.onRemoveClick){
|
||||||
this.props.onRemoveClick(tag);
|
this.props.onRemoveClick(tagId);
|
||||||
}
|
|
||||||
}
|
|
||||||
onTagSelected(tag) {
|
|
||||||
if(this.props.onTagSelected){
|
|
||||||
this.props.onTagSelected(tag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTagSelected(tagId) {
|
||||||
|
if(this.props.onTagSelected){
|
||||||
|
this.props.onTagSelected(tagId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default TagSelector;
|
export default TagSelector;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
cursor: text;
|
cursor: text;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid $grey;
|
border: 1px solid $grey;
|
||||||
|
min-height: 38px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
|
@ -17,34 +18,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__selected-tags {
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: white;
|
|
||||||
padding: 3px 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__selected-tag {
|
|
||||||
color: white;
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-left: 5px;
|
|
||||||
padding: 3px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__selected-tag {
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
&-remove {
|
|
||||||
cursor: pointer;
|
|
||||||
margin-left: 10px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $light-grey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tag-options {
|
&__tag-options {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: $dark-grey;
|
color: $dark-grey;
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Icon from 'core-components/icon';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
class Tag extends React.Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
name: React.PropTypes.string,
|
||||||
|
color: React.PropTypes.string,
|
||||||
|
showDeleteButton: React.PropTypes.bool,
|
||||||
|
onRemoveClick: React.PropTypes.func,
|
||||||
|
size: React.PropTypes.oneOf(['small','medium'])
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={this.getClass()} style={{backgroundColor:this.props.color}} onClick={event => event.stopPropagation()} >
|
||||||
|
<span className="tag__name">{this.props.name}</span>
|
||||||
|
{this.props.showDeleteButton ? this.renderRemoveButton() : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRemoveButton() {
|
||||||
|
return (
|
||||||
|
<span onClick={this.props.onRemoveClick} className="tag__remove" >
|
||||||
|
<Icon name="times-circle" size="small"/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getClass() {
|
||||||
|
let classes = {
|
||||||
|
'tag': true,
|
||||||
|
'tag_small': this.props.size === 'small',
|
||||||
|
'tag_medium': this.props.size === 'medium'
|
||||||
|
};
|
||||||
|
|
||||||
|
return classNames(classes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Tag;
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '../scss/vars';
|
||||||
|
|
||||||
|
.tag{
|
||||||
|
color: white;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-left: 5px;
|
||||||
|
padding: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&__remove {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $light-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_small {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,6 +62,8 @@ class SessionStore {
|
||||||
this.setItem('allow-attachments', configs['allow-attachments']);
|
this.setItem('allow-attachments', configs['allow-attachments']);
|
||||||
this.setItem('maintenance-mode', configs['maintenance-mode']);
|
this.setItem('maintenance-mode', configs['maintenance-mode']);
|
||||||
this.setItem('max-size', configs['max-size']);
|
this.setItem('max-size', configs['max-size']);
|
||||||
|
this.setItem('tags', configs['tags']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigs() {
|
getConfigs() {
|
||||||
|
@ -78,6 +80,7 @@ class SessionStore {
|
||||||
'allow-attachments': (this.getItem('allow-attachments') * 1),
|
'allow-attachments': (this.getItem('allow-attachments') * 1),
|
||||||
'maintenance-mode': (this.getItem('maintenance-mode') * 1),
|
'maintenance-mode': (this.getItem('maintenance-mode') * 1),
|
||||||
'max-size': this.getItem('max-size'),
|
'max-size': this.getItem('max-size'),
|
||||||
|
'tags': this.getItem('tags')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue