@@ -160,59 +164,114 @@ class TicketViewer extends React.Component {
}
renderEditableHeaders() {
- const ticket = this.props.ticket;
- const departments = this.getDepartmentsForTransfer();
+ const { userStaff, ticket } = this.props;
+ const filtersOnlyWithAuthor = {
+ authors: [
+ {
+ id: ticket.author.id*1,
+ isStaff: ticket.author.staff*1
+ }
+ ]
+ };
return (
-
{i18n('DEPARTMENT')}
-
-
+
+
{i18n('DEPARTMENT')}
+
+ {
+ this.state.editDepartment ?
+ this.renderEditDepartment() :
+ ticket.department.name
+ }
+
+ {userStaff ? this.renderEditOption("Department") : null}
-
{i18n('TAGS')}
-
-
+
+
{i18n('TAGS')}
+
+ {
+ this.state.editTags ?
+ this.renderEditTags() :
+ this.renderTags()
+ }
+
+ {userStaff ? this.renderEditOption("Tags") : null}
-
{i18n('OWNER')}
-
- {this.renderAssignStaffList()}
+
+
{i18n('OWNER')}
+
+ {this.renderOwnerNode()}
+
+ {userStaff ? this.renderEditOption("Owner") : null}
-
-
{i18n('AUTHOR')}
-
{ticket.author.name}
-
+
+
+
{i18n('STATUS')}
+
+ {this.state.editStatus ? this.renderEditStatus() : (ticket.closed ? i18n('CLOSED') : i18n('OPENED'))}
+
+
+ {userStaff ? this.renderEditOption("Status") : null}
+
);
}
+ renderEditTags() {
+ const {
+ tags,
+ ticket
+ } = this.props;
+
+ return (
+
+
+ {this.renderCancelButton("Tags")}
+
+ );
+ }
+
+ renderEditStatus() {
+ return (
+
+ {this.renderCancelButton("Status")}
+ {this.props.ticket.closed ?
+ :
+ }
+
+ );
+ }
+
renderHeaders() {
const ticket = this.props.ticket;
@@ -229,10 +288,9 @@ class TicketViewer extends React.Component {
{i18n('TAGS')}
-
{ticket.tags.length ? ticket.tags.map((tagName,index) => {
- let tag = _.find(this.props.tags, {name:tagName});
- return
- }) : i18n('NONE')}
+
+ {this.renderTags()}
+
@@ -245,7 +303,7 @@ class TicketViewer extends React.Component {
{i18n('STATUS')}
- {i18n((this.props.ticket.closed) ? 'CLOSED' : 'OPENED')}
+ {i18n((ticket.closed) ? 'CLOSED' : 'OPENED')}
@@ -253,53 +311,137 @@ class TicketViewer extends React.Component {
);
}
- renderOwnerNode() {
- let ownerNode = null;
+ renderTags() {
+ const {
+ ticket,
+ tags
+ } = this.props;
+ const TAGS = (
+ ticket.tags.length ?
+ ticket.tags.map((tagName, index) => {
+ const tag = _.find(tags, {name: tagName});
+ return
+ }) :
+ i18n('NONE')
+ );
- if (this.props.assignmentAllowed) {
- ownerNode = this.renderAssignStaffList();
- } else {
- ownerNode = (this.props.ticket.owner) ? this.props.ticket.owner.name : i18n('NONE')
- }
-
- return ownerNode;
+ return TAGS;
}
- renderAssignStaffList() {
+ renderOwnerNode() {
+ const {
+ assignmentAllowed,
+ ticket
+ } = this.props;
+ const filtersOnlyWithOwner = ticket.owner && {owners: [ticket.owner.id*1]};
+ let ownerNode = null;
+
+ if(assignmentAllowed && ticket.owner) {
+ ownerNode = (
+
+ {ticket.owner.name}
+
+ );
+ } else {
+ ownerNode = (
+
+ {(ticket.owner) ? ticket.owner.name : i18n('NONE')}
+
+ );
+ }
+
+ return (assignmentAllowed && this.state.editOwner) ? this.renderEditOwner() : ownerNode;
+ }
+
+ renderEditOwner() {
const items = this.getStaffAssignmentItems();
- const ownerId = this.props.ticket.owner && this.props.ticket.owner.id*1;
+ const { ticket } = this.props;
+ const ownerId = ticket.owner && ticket.owner.id*1;
let selectedIndex = _.findIndex(items, {id: ownerId});
selectedIndex = (selectedIndex !== -1) ? selectedIndex : 0;
return (
-
+
+
+ {this.renderCancelButton("Owner")}
+
);
}
+ renderEditDepartment() {
+ const { ticket } = this.props;
+ const departments = this.getDepartmentsForTransfer();
+
+ return (
+
+
+ {this.renderCancelButton("Department")}
+
+ );
+ }
+
+ renderEditTitleOption() {
+ return(
+
+ this.setState({editTitle: true})} />
+
+ )
+ }
+
+ renderEditOption(option) {
+ return(
+
+ this.setState({["edit"+option]: true})} />
+
+ );
+ }
+
+ renderEditedTitleText(){
+ return(
+
{i18n('TITLE_EDITED')}
+ )
+ }
+
+ renderCancelButton(option) {
+ return
+ }
+
renderTicketEvent(options, index) {
- if (this.props.userStaff && typeof options.content === 'string') {
+ const {
+ userStaff,
+ ticket,
+ userId,
+ allowAttachments
+ } = this.props;
+
+ if(userStaff && typeof options.content === 'string') {
options.content = MentionsParser.parse(options.content);
}
+
return (
+ allowAttachments={allowAttachments} />
);
}
renderResponseField() {
+ const { allowAttachments } = this.props;
+
return (
-
- {(this.props.allowAttachments) ?
: null}
+
+ {allowAttachments ?
: null}
{i18n('RESPOND_TICKET')}
-
{(this.showDeleteButton())? : null}
@@ -330,7 +471,7 @@ class TicketViewer extends React.Component {
renderCustomResponses() {
let customResponsesNode = null;
- if (this.props.customResponses && this.props.editable) {
+ if(this.props.customResponses && this.props.editable) {
let customResponses = this.props.customResponses.map((customResponse) => {
return {
content: customResponse.name
@@ -343,7 +484,7 @@ class TicketViewer extends React.Component {
customResponsesNode = (
-
+
);
}
@@ -352,10 +493,10 @@ class TicketViewer extends React.Component {
}
renderPrivate() {
- if (this.props.userStaff) {
+ if(this.props.userStaff) {
return (
-
+
);
@@ -370,6 +511,13 @@ class TicketViewer extends React.Component {
);
}
+ searchTickets(filters) {
+ const SEARCH_TICKETS_PATH = '/admin/panel/tickets/search-tickets';
+ const urlQuery = filters && searchTicketsUtils.getFiltersForURL({filters});
+
+ return urlQuery && `${SEARCH_TICKETS_PATH}${urlQuery}`;
+ }
+
getCommentFormProps() {
return {
onSubmit: this.onSubmit.bind(this),
@@ -424,10 +572,18 @@ class TicketViewer extends React.Component {
}
onReopenClick() {
+ this.setState({
+ editStatus: false
+ });
+
AreYouSure.openModal(null, this.reopenTicket.bind(this));
}
onCloseTicketClick(event) {
+ this.setState({
+ editStatus: false
+ });
+
event.preventDefault();
AreYouSure.openModal(null, this.closeTicket.bind(this));
}
@@ -646,47 +802,77 @@ class TicketViewer extends React.Component {
}
onTicketModification() {
- if (this.props.onChange) {
- this.props.onChange();
+ const { onChange } = this.props;
+ if(onChange) {
+ onChange();
}
}
getStaffAssignmentItems() {
- const {staffMembers, userDepartments, userId, ticket} = this.props;
+ const {
+ userDepartments,
+ userId,
+ ticket
+ } = this.props;
const ticketDepartmentId = ticket.department.id;
+ const FIRST_ITEM = 0;
let staffAssignmentItems = [
- {content: 'None', id: 0}
+ {content: i18n('NONE'), contentOnSelected: i18n('NONE'), id: 0}
];
if(_.some(userDepartments, {id: ticketDepartmentId})) {
- staffAssignmentItems.push({content: i18n('ASSIGN_TO_ME'), id: userId});
+ staffAssignmentItems.push({
+ content: i18n('ASSIGN_TO_ME'),
+ contentOnSelected: this.getStaffList({onlyMe: true})[FIRST_ITEM].name,
+ id: userId
+ });
}
staffAssignmentItems = staffAssignmentItems.concat(
_.map(
- _.filter(staffMembers, ({id, departments}) => {
- return (id != userId) && _.some(departments, {id: ticketDepartmentId});
- }),
- ({id, name}) => ({content: name, id: id*1})
+ this.getStaffList({onlyMe: false}),
+ ({id, name}) => ({content: name, contentOnSelected: name, id: id*1})
)
);
return staffAssignmentItems;
}
+ getStaffList(onlyMeObject) {
+ const {
+ userId,
+ staffMembers,
+ ticket
+ } = this.props;
+
+ return _.filter(staffMembers, ({id, departments}) => {
+ const idComparer = onlyMeObject.onlyMe ? (id == userId) : (id != userId);
+
+ return idComparer && _.some(departments, {id: ticket.department.id});
+ })
+ }
+
getDepartmentsForTransfer() {
return this.props.ticket.author.staff ? SessionStore.getDepartments() : this.getPublicDepartments();
}
showDeleteButton() {
- if(!this.props.ticket.owner) {
- if(this.props.userLevel === 3) return true;
- if(this.props.userId == this.props.ticket.author.id*1) {
- if((this.props.userStaff && this.props.ticket.author.staff) || (!this.props.userStaff && !this.props.ticket.author.staff)){
+ const {
+ ticket,
+ userLevel,
+ userId,
+ userStaff
+ } = this.props;
+
+ if(!ticket.owner) {
+ if(userLevel === 3) return true;
+ if(userId == ticket.author.id*1) {
+ if((userStaff && ticket.author.staff) || (!userStaff && !ticket.author.staff)){
return true;
}
}
}
+
return false;
}
}
diff --git a/client/src/app-components/ticket-viewer.scss b/client/src/app-components/ticket-viewer.scss
index 79c3cd8e..55d695fa 100644
--- a/client/src/app-components/ticket-viewer.scss
+++ b/client/src/app-components/ticket-viewer.scss
@@ -26,15 +26,34 @@
margin: 0 10px;
}
- &__edit-title-icon {
- color: #414A59;
+ &__edit-icon {
right: 12px;
margin: 0 10px;
+ color: $light-grey;
+
&:hover {
cursor:pointer;
}
}
+ &__edit-title-icon {
+ color: $primary-blue;
+ right: 12px;
+ margin: 0 10px;
+
+ &:hover {
+ cursor:pointer;
+ }
+ }
+
+ &__edit-status__buttons {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 10px;
+ }
+
&___input-edit-title {
color: black;
align-items:center;
@@ -87,10 +106,24 @@
&-container-editable {
display: flex;
- flex-direction: column;
- justify-content: space-around;
- align-items: center;
+ flex-direction: row;
+ justify-content: center;
+ align-items: flex-start;
min-width: 300px;
+
+ &:hover {
+ .ticket-viewer__edit-icon {
+ color: $primary-blue;
+ }
+ }
+
+ &__column {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ align-items: center;
+ min-width: 170px;
+ }
}
&-header {
@@ -100,7 +133,7 @@
&-value {
color: $secondary-blue;
padding-bottom: 10px;
-
+ width: 100%;
}
}
diff --git a/client/src/core-components/autocomplete.scss b/client/src/core-components/autocomplete.scss
index 45cc37ee..b567b074 100644
--- a/client/src/core-components/autocomplete.scss
+++ b/client/src/core-components/autocomplete.scss
@@ -7,14 +7,14 @@
&__drop-down {
.drop-down__current-item {
cursor: text;
- background-color: white;
+ background-color: $very-light-grey;
border: 1px solid $grey;
min-height: 38px;
&:focus-within {
outline: none;
border-color: $primary-blue;
- background-color: white;
+ background-color: $very-light-grey;
}
}
diff --git a/client/src/core-components/drop-down.js b/client/src/core-components/drop-down.js
index acc76beb..fc0f2a52 100644
--- a/client/src/core-components/drop-down.js
+++ b/client/src/core-components/drop-down.js
@@ -89,7 +89,7 @@ class DropDown extends React.Component {
}
renderCurrentItem() {
- let item = this.props.items[this.getSelectedIndex()];
+ const item = this.props.items[this.getSelectedIndex()];
let iconNode = null;
if (item.icon) {
@@ -98,7 +98,7 @@ class DropDown extends React.Component {
return (
- {iconNode}{item.content}
+ {iconNode}{item.contentOnSelected ? item.contentOnSelected : item.content}
);
}