import React from 'react'; import _ from 'lodash'; import {connect} from 'react-redux'; import AdminDataActions from 'actions/admin-data-actions'; import i18n from 'lib-app/i18n'; import API from 'lib-app/api-call'; import SessionStore from 'lib-app/session-store'; import MentionsParser from 'lib-app/mentions-parser'; import history from 'lib-app/history'; 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'; import Message from 'core-components/message'; import Icon from 'core-components/icon'; import TextEditor from 'core-components/text-editor'; import InfoTooltip from 'core-components/info-tooltip'; 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 { static propTypes = { ticket: React.PropTypes.object, onChange: React.PropTypes.func, editable: React.PropTypes.bool, customResponses: React.PropTypes.array, assignmentAllowed: React.PropTypes.bool, staffMembers: React.PropTypes.array, staffMembersLoaded: React.PropTypes.bool, allowAttachments: React.PropTypes.bool, userId: React.PropTypes.number, userStaff: React.PropTypes.bool, userDepartments: React.PropTypes.array, userLevel: React.PropTypes.number, tags: React.PropTypes.array }; static defaultProps = { tags: [], editable: false, ticket: { author: {}, department: {}, comments: [], edited: null } }; state = { loading: false, commentValue: TextEditor.createEmpty(), commentEdited: false, commentPrivate: false, edit: false, editId: 0, tagSelectorLoading: false, editTitle: false, newTitle: this.props.ticket.title, editTitleError: false, }; componentDidMount() { if(!this.props.staffMembersLoaded && this.props.userStaff) { this.props.dispatch(AdminDataActions.retrieveStaffMembers()); } } render() { const ticket = this.props.ticket; return (
{this.state.editTitle ? this.renderEditableTitle() : this.renderTitleHeader()} {this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
{ticket.events && ticket.events.map(this.renderTicketEvent.bind(this))}
{(!this.props.ticket.closed && (this.props.editable || !this.props.assignmentAllowed)) ? this.renderResponseField() : (this.showDeleteButton())? : null}
); } renderTitleHeader() { const {ticket, userStaff, userId} = this.props; const {ticketNumber, title, author, editedTitle, language} = ticket; return(
#{ticketNumber} {title} {((author.id == userId && author.staff == userStaff) || userStaff) ? this.renderEditTitleOption() : null} {editedTitle ? this.renderEditedTitleText() : null }
) } renderEditedTitleText(){ return(
{i18n('TITLE_EDITED')}
) } renderEditTitleOption() { return( this.setState({editTitle: true})} /> ) } renderEditableTitle(){ return(
this.setState({newTitle: e.target.value })} />
) } renderEditableHeaders() { const ticket = this.props.ticket; const departments = this.getDepartmentsForTransfer(); const priorities = { 'low': 0, 'medium': 1, 'high': 2 }; const priorityList = [ {content: i18n('LOW')}, {content: i18n('MEDIUM')}, {content: i18n('HIGH')} ]; return (
{i18n('DEPARTMENT')}
{i18n('AUTHOR')}
{i18n('TAGS')}
{ticket.author.name}
{i18n('PRIORITY')}
{i18n('OWNER')}
{i18n('STATUS')}
{this.renderAssignStaffList()}
{ticket.closed ? : i18n('OPENED')}
); } renderHeaders() { const ticket = this.props.ticket; const priorities = { 'low': 'LOW', 'medium': 'MEDIUM', 'high': 'HIGH' }; return (
{i18n('DEPARTMENT')}
{i18n('AUTHOR')}
{i18n('TAGS')}
{ticket.department.name}
{ticket.author.name}
{ticket.tags.length ? ticket.tags.map((tagName,index) => { let tag = _.find(this.props.tags, {name:tagName}); return }) : i18n('NONE')}
{i18n('PRIORITY')}
{i18n('OWNER')}
{i18n('STATUS')}
{i18n(priorities[this.props.ticket.priority || 'low'])}
{this.renderOwnerNode()}
{i18n((this.props.ticket.closed) ? 'CLOSED' : 'OPENED')}
); } renderOwnerNode() { let ownerNode = null; if (this.props.assignmentAllowed) { ownerNode = this.renderAssignStaffList(); } else { ownerNode = (this.props.ticket.owner) ? this.props.ticket.owner.name : i18n('NONE') } return ownerNode; } renderAssignStaffList() { const items = this.getStaffAssignmentItems(); const ownerId = this.props.ticket.owner && this.props.ticket.owner.id; let selectedIndex = _.findIndex(items, {id: ownerId}); selectedIndex = (selectedIndex !== -1) ? selectedIndex : 0; return ( ); } renderTicketEvent(options, index) { if (this.props.userStaff && typeof options.content === 'string') { options.content = MentionsParser.parse(options.content); } return ( ); } renderResponseField() { return (
{i18n('RESPOND')}
{this.renderCustomResponses()} {this.renderPrivate()}
{(this.props.allowAttachments) ? : null}
{i18n('RESPOND_TICKET')}
{(this.showDeleteButton())? : null}
{(this.state.commentError) ? this.renderCommentError() : null}
); } renderCustomResponses() { let customResponsesNode = null; if (this.props.customResponses && this.props.editable) { let customResponses = this.props.customResponses.map((customResponse) => { return { content: customResponse.name }; }); customResponses.unshift({ content: i18n('SELECT_CUSTOM_RESPONSE') }); customResponsesNode = (
); } return customResponsesNode; } renderPrivate() { if (this.props.userStaff) { return (
); } else { return null; } } renderCommentError() { return ( {i18n('TICKET_COMMENT_ERROR')} ); } getCommentFormProps() { return { onSubmit: this.onSubmit.bind(this), loading: this.state.loading, onChange: (formState) => {this.setState({ commentValue: formState.content, commentFile: formState.file, commentEdited: true, commentPrivate: formState.private })}, values: { 'content': this.state.commentValue, 'file': this.state.commentFile, 'private': this.state.commentPrivate } }; } getPublicDepartments() { return _.filter(SessionStore.getDepartments(),d => !(d.private*1)); } onDepartmentDropdownChanged(event) { AreYouSure.openModal(null, this.changeDepartment.bind(this, event.index)); } onPriorityDropdownChanged(event) { AreYouSure.openModal(null, this.changePriority.bind(this, event.index)); } onAssignmentChange(event) { AreYouSure.openModal(null, this.assingTo.bind(this, event.index)); } assingTo(index) { const id = this.getStaffAssignmentItems()[index].id; const {ticketNumber, owner} = this.props.ticket; let APICallPromise = new Promise(resolve => resolve()); if(owner) { APICallPromise.then(() => API.call({ path: '/staff/un-assign-ticket', data: { ticketNumber } })); } if(id !== 0) { APICallPromise.then(() => API.call({ path: '/staff/assign-ticket', data: { ticketNumber, staffId: id } })); } APICallPromise.then(this.onTicketModification.bind(this)); } onReopenClick() { AreYouSure.openModal(null, this.reopenTicket.bind(this)); } onCloseTicketClick(event) { event.preventDefault(); AreYouSure.openModal(null, this.closeTicket.bind(this)); } onDeleteTicketClick(event) { event.preventDefault(); AreYouSure.openModal(null, this.deleteTicket.bind(this)); } changeTitle(){ API.call({ path: '/ticket/edit-title', data: { ticketNumber: this.props.ticket.ticketNumber, title: this.state.newTitle } }).then(() => { this.setState({ editTitle: false, editTitleError: false }); this.onTicketModification(); }).catch((result) => { this.setState({ editTitleError: i18n(result.message) }) }); } reopenTicket() { API.call({ path: '/ticket/re-open', data: { ticketNumber: this.props.ticket.ticketNumber } }).then(this.onTicketModification.bind(this)); } closeTicket() { API.call({ path: '/ticket/close', data: { ticketNumber: this.props.ticket.ticketNumber } }).then(this.onTicketModification.bind(this)); } deleteTicket() { API.call({ path: '/ticket/delete', data: { ticketNumber: this.props.ticket.ticketNumber } }).then((result) => { this.onTicketModification(result); history.push('/admin/panel/tickets/my-tickets/'); }); } changeDepartment(index) { API.call({ path: '/ticket/change-department', data: { ticketNumber: this.props.ticket.ticketNumber, departmentId: this.getDepartmentsForTransfer()[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)); } addTag(tag) { this.setState({ tagSelectorLoading: true, }) API.call({ path: '/ticket/add-tag', data: { ticketNumber: this.props.ticket.ticketNumber, tagId: tag } }) .then(() => { this.setState({ tagSelectorLoading: false, }); this.onTicketModification(); }) .catch(() => this.setState({ tagSelectorLoading: false, })) } removeTag(tag) { this.setState({ tagSelectorLoading: true, }); API.call({ path: '/ticket/remove-tag', data: { ticketNumber: this.props.ticket.ticketNumber, tagId: tag } }).then(() => { this.setState({ tagSelectorLoading: false, }); this.onTicketModification(); }).catch(() => this.setState({ tagSelectorLoading: false, })) } onCustomResponsesChanged({index}) { let replaceContentWithCustomResponse = () => { this.setState({ commentValue: TextEditor.getEditorStateFromHTML(this.props.customResponses[index-1].content || ''), commentEdited: false }); }; if (this.state.commentEdited && index) { AreYouSure.openModal(null, replaceContentWithCustomResponse); } else { replaceContentWithCustomResponse(); } } onToggleEdit(ticketEventId){ this.setState({ edit: !this.state.edit, editId: ticketEventId }) } onEdit(ticketeventid,{content}) { this.setState({ loading: true }); const data = {}; if(ticketeventid){ data.ticketEventId = ticketeventid }else{ data.ticketNumber = this.props.ticket.ticketNumber } API.call({ path: '/ticket/edit-comment', data: _.extend( data, TextEditor.getContentFormData(content) ) }).then(this.onEditCommentSuccess.bind(this), this.onFailCommentFail.bind(this)); } onEditCommentSuccess() { this.setState({ loading: false, commentError: false, commentEdited: false, edit:false }); this.onTicketModification(); } onFailCommentFail() { this.setState({ loading: false, commentError: true }); } onSubmit(formState) { this.setState({ loading: true }); API.call({ path: '/ticket/comment', dataAsForm: true, data: _.extend({ ticketNumber: this.props.ticket.ticketNumber }, formState, {private: formState.private ? 1 : 0}, TextEditor.getContentFormData(formState.content)) }).then(this.onCommentSuccess.bind(this), this.onCommentFail.bind(this)); } onCommentSuccess() { this.setState({ loading: false, commentValue: TextEditor.createEmpty(), commentError: false, commentEdited: false }); this.onTicketModification(); } onCommentFail() { this.setState({ loading: false, commentError: true }); } onTicketModification() { if (this.props.onChange) { this.props.onChange(); } } getStaffAssignmentItems() { const {staffMembers, userDepartments, userId, ticket} = this.props; const ticketDepartmentId = ticket.department.id; let staffAssignmentItems = [ {content: 'None', id: 0} ]; if(_.any(userDepartments, {id: ticketDepartmentId})) { staffAssignmentItems.push({content: i18n('ASSIGN_TO_ME'), id: userId}); } staffAssignmentItems = staffAssignmentItems.concat( _.map( _.filter(staffMembers, ({id, departments}) => { return (id != userId) && _.any(departments, {id: ticketDepartmentId}); }), ({id, name}) => ({content: name, id}) ) ); return staffAssignmentItems; } 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) { if((this.props.userStaff && this.props.ticket.author.staff) || (!this.props.userStaff && !this.props.ticket.author.staff)){ return true; } } } return false; } } export default connect((store) => { return { userId: store.session.userId, userStaff: store.session.staff, userDepartments: store.session.userDepartments, staffMembers: store.adminData.staffMembers, staffMembersLoaded: store.adminData.staffMembersLoaded, allowAttachments: store.config['allow-attachments'], userSystemEnabled: store.config['user-system-enabled'], userLevel: store.session.userLevel, tags: store.config['tags'] }; })(TicketViewer);