diff --git a/client/src/actions/config-actions.js b/client/src/actions/config-actions.js index cbde584c..0c2617e7 100644 --- a/client/src/actions/config-actions.js +++ b/client/src/actions/config-actions.js @@ -26,5 +26,15 @@ export default { type: 'CHANGE_LANGUAGE', payload: newLanguage }; + }, + + updateData() { + return { + type: 'UPDATE_DEPARTMENTS', + payload: API.call({ + path: '/system/get-settings', + data: {} + }) + }; } }; \ No newline at end of file diff --git a/client/src/app-components/are-you-sure.js b/client/src/app-components/are-you-sure.js index 13c86819..82b569c0 100644 --- a/client/src/app-components/are-you-sure.js +++ b/client/src/app-components/are-you-sure.js @@ -6,7 +6,7 @@ import ModalContainer from 'app-components/modal-container'; class AreYouSure extends React.Component { static propTypes = { - description: React.PropTypes.string, + description: React.PropTypes.node, onYes: React.PropTypes.func }; diff --git a/client/src/app/admin/panel/staff/admin-panel-departments.js b/client/src/app/admin/panel/staff/admin-panel-departments.js index 768add5f..2b9db331 100644 --- a/client/src/app/admin/panel/staff/admin-panel-departments.js +++ b/client/src/app/admin/panel/staff/admin-panel-departments.js @@ -1,14 +1,221 @@ import React from 'react'; +import _ from 'lodash'; +import {connect} from 'react-redux'; +import RichTextEditor from 'react-rte-browserify'; + +import i18n from 'lib-app/i18n'; +import API from 'lib-app/api-call'; +import ConfigActions from 'actions/config-actions'; + +import AreYouSure from 'app-components/are-you-sure'; + +import InfoTooltip from 'core-components/info-tooltip'; +import Button from 'core-components/button'; +import Header from 'core-components/header'; +import Listing from 'core-components/listing'; +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'; class AdminPanelDepartments extends React.Component { + static defaultProps = { + items: [] + }; + + state = { + formLoading: false, + selectedIndex: -1, + selectedDropDownIndex: 0, + edited: false, + errors: {}, + form: { + title: '', + content: RichTextEditor.createEmptyValue(), + language: 'en' + } + }; render() { return ( -
- /admin/panel/staff/departments +
+
+
+
+ +
+
+
+ + + {i18n((this.state.selectedIndex !== -1) ? 'UPDATE_DEPARTMENT' : 'ADD_DEPARTMENT')} + + + {(this.state.selectedIndex !== -1 && this.props.departments.length) ? this.renderOptionalButtons() : null} +
+
); } + + renderOptionalButtons() { + return ( +
+
+ +
+
+ +
+
+ ); + } + + renderDelete() { + return ( +
+ {i18n('WILL_DELETE_DEPARTMENT')} +
+ {i18n('TRANSFER_TICKETS_TO')} + index !== this.state.selectedIndex).map(department => { + return { + content: department.name + }; + })} onChange={(department, index) => this.setState({selectedDropDownIndex: index})} size="medium"/> +
+
+ ); + } + + getListingProps() { + return { + className: 'admin-panel-departments__list', + title: i18n('DEPARTMENTS'), + items: this.props.departments.map(department => { + return { + content: ( + + {department.name} + {(!department.owners) ? ( + + + + ) : null} + + ) + }; + }), + selectedIndex: this.state.selectedIndex, + enableAddNew: true, + onChange: this.onItemChange.bind(this), + onAddClick: this.onItemChange.bind(this, -1) + }; + } + + getFormProps() { + return { + values: this.state.form, + errors: this.state.errors, + loading: this.state.formLoading, + onChange: (form) => {this.setState({form, edited: true})}, + onValidateErrors: (errors) => {this.setState({errors})}, + onSubmit: this.onFormSubmit.bind(this) + }; + } + + onItemChange(index) { + if(this.state.edited) { + AreYouSure.openModal(i18n('WILL_LOSE_CHANGES'), this.updateForm.bind(this, index)); + } else { + this.updateForm(index); + } + } + + onFormSubmit(form) { + this.setState({formLoading: true, edited: false}); + + if(this.state.selectedIndex !== -1) { + API.call({ + path: '/staff/edit-department', + data: { + departmentId: this.getCurrentDepartment().id, + name: form.name + } + }).then(() => { + this.setState({formLoading: false}); + this.retrieveDepartments(); + }).catch(this.onItemChange.bind(this, -1)); + } else { + API.call({ + path: '/staff/add-department', + data: { + name: form.title + } + }).then(() => { + this.retrieveDepartments(); + this.onItemChange(-1); + }).catch(this.onItemChange.bind(this, -1)); + } + } + + onDiscardChangesClick() { + this.onItemChange(this.state.selectedIndex); + } + + onDeleteClick() { + this.setState({ + selectedDropDownIndex: 0 + }); + + AreYouSure.openModal(this.renderDelete(), this.deleteDepartment.bind(this)); + } + + deleteDepartment() { + API.call({ + path: '/staff/delete-department', + data: { + departmentId: this.getCurrentDepartment().id, + transferDepartmentId: this.getDropDownItemId() + } + }).then(() => { + this.retrieveDepartments(); + this.onItemChange(-1); + }); + } + + updateForm(index) { + let form = _.clone(this.state.form); + let department = this.getCurrentDepartment(index); + + form.name = (department && department.name) || ''; + + this.setState({ + selectedIndex: index, + edited: false, + formLoading: false, + form: form, + errors: {} + }); + } + + retrieveDepartments() { + this.props.dispatch(ConfigActions.updateData()); + this.setState({ + edited: false + }); + } + + getCurrentDepartment(index) { + return this.props.departments[(index == undefined) ? this.state.selectedIndex : index]; + } + + getDropDownItemId() { + return this.props.departments.filter((department, index) => index !== this.state.selectedIndex)[this.state.selectedDropDownIndex].id; + } } -export default AdminPanelDepartments; \ No newline at end of file +export default connect((store) => { + return { + departments: store.config.departments + }; +})(AdminPanelDepartments); \ No newline at end of file diff --git a/client/src/app/admin/panel/staff/admin-panel-departments.scss b/client/src/app/admin/panel/staff/admin-panel-departments.scss new file mode 100644 index 00000000..05a2441f --- /dev/null +++ b/client/src/app/admin/panel/staff/admin-panel-departments.scss @@ -0,0 +1,41 @@ +@import "../../../../scss/vars"; + +.admin-panel-departments { + + + &__list { + position: relative; + } + + &__update-name-button { + float: left; + } + + &__optional-buttons { + float: right; + } + + &__discard-button, + &__delete-button { + display: inline-block; + margin-left: 10px; + } + + &__warning { + position: absolute; + right: 10px; + } + + &__transfer-tickets { + margin-top: 40px; + } + + &__transfer-tickets-title { + margin-right: 20px; + color: $primary-black; + } + + &__transfer-tickets-drop-down { + display: inline-block; + } +} \ No newline at end of file diff --git a/client/src/core-components/listing.js b/client/src/core-components/listing.js index b66da124..3211333d 100644 --- a/client/src/core-components/listing.js +++ b/client/src/core-components/listing.js @@ -1,4 +1,5 @@ import React from 'react'; +import classNames from 'classnames'; import Menu from 'core-components/menu'; import Button from 'core-components/button'; @@ -20,7 +21,7 @@ class Listing extends React.Component { render() { return ( -
+
{this.props.title}
@@ -42,6 +43,16 @@ class Listing extends React.Component { ); } + getClass() { + let classes = { + 'listing': true + }; + + classes[this.props.className] = (this.props.className); + + return classNames(classes); + } + } export default Listing; \ No newline at end of file diff --git a/client/src/data/fixtures/staff-fixtures.js b/client/src/data/fixtures/staff-fixtures.js index cf2d24e7..458ac688 100644 --- a/client/src/data/fixtures/staff-fixtures.js +++ b/client/src/data/fixtures/staff-fixtures.js @@ -14,8 +14,8 @@ module.exports = [ level: 1, staff: true, departments: [ - {id: 1, name: 'Sales Support'}, - {id: 2, name: 'Technical Issues'} + {id: 1, name: 'Sales Support', owners: 2}, + {id: 2, name: 'Technical Issues', owners: 5} ], tickets: [ { @@ -24,7 +24,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 5 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -143,7 +144,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 5 }, date: '20160415', file: 'http://www.opensupports.com/some_file.zip', @@ -259,7 +261,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 5 }, date: '20150409', file: 'http://www.opensupports.com/some_file.zip', @@ -375,7 +378,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 1, - name: 'Sales Support' + name: 'Sales Support', + owners: 2 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -413,7 +417,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 5 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -532,7 +537,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 5 }, date: '20160415', file: 'http://www.opensupports.com/some_file.zip', @@ -648,7 +654,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 5 }, date: '20150409', file: 'http://www.opensupports.com/some_file.zip', @@ -764,7 +771,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 1, - name: 'Sales Support' + name: 'Sales Support', + owners: 2 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -801,7 +809,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 1, - name: 'Sales Support' + name: 'Sales Support', + owners: 2 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -827,7 +836,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 1, - name: 'Sales Support' + name: 'Sales Support', + owners: 2 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -853,7 +863,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 2, - name: 'Technical Issues' + name: 'Technical Issues', + owners: 2 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -891,7 +902,8 @@ module.exports = [ content: 'I had a problem with the installation of the php server', department: { id: 1, - name: 'Sales Support' + name: 'Sales Support', + owners: 2 }, date: '20160416', file: 'http://www.opensupports.com/some_file.zip', @@ -1035,5 +1047,35 @@ module.exports = [ data: {} }; } + }, + { + path: '/staff/add-department', + time: 100, + response: function () { + return { + status: 'success', + data: {} + }; + } + }, + { + path: '/staff/edit-department', + time: 100, + response: function () { + return { + status: 'success', + data: {} + }; + } + }, + { + path: '/staff/delete-department', + time: 100, + response: function () { + return { + status: 'success', + data: {} + }; + } } ]; \ No newline at end of file diff --git a/client/src/data/fixtures/system-fixtures.js b/client/src/data/fixtures/system-fixtures.js index b630d7be..258c2112 100644 --- a/client/src/data/fixtures/system-fixtures.js +++ b/client/src/data/fixtures/system-fixtures.js @@ -9,9 +9,9 @@ module.exports = [ 'language': 'en', 'reCaptchaKey': '6LfM5CYTAAAAAGLz6ctpf-hchX2_l0Ge-Bn-n8wS', 'departments': [ - {id: 1, name: 'Sales Support'}, - {id: 2, name: 'Technical Issues'}, - {id: 3, name: 'System and Administration'} + {id: 1, name: 'Sales Support', owners: 2}, + {id: 2, name: 'Technical Issues', owners: 5}, + {id: 3, name: 'System and Administration', owners: 0} ] } }; diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index acb99b74..0805e711 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -111,6 +111,9 @@ export default { 'UPDATE_LEVEL': 'Update level', 'UPDATE_DEPARTMENTS': 'Update departments', 'EDIT_STAFF': 'Edit staff member', + 'ADD_DEPARTMENT': 'Add department', + 'UPDATE_DEPARTMENT': 'Update department', + 'TRANSFER_TICKETS_TO': 'Transfer tickets to', //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.', @@ -137,6 +140,7 @@ export default { 'ADD_STAFF_DESCRIPTION': 'Here you can add staff members to your teams.', 'EDIT_STAFF_DESCRIPTION': 'Here you can edit information about a staff member.', 'MY_ACCOUNT_DESCRIPTION': 'Here you can edit information about you.', + 'DEPARTMENTS_DESCRIPTION': 'A department is a group where the tickets can go. They are used to categorize the tickets. You can assign them to other staff members.', //ERRORS 'EMAIL_OR_PASSWORD': 'Email or password invalid', @@ -163,5 +167,7 @@ export default { 'PASSWORD_CHANGED': 'Password has been changed successfully', 'OLD_PASSWORD_INCORRECT': 'Old password is incorrect', 'WILL_LOSE_CHANGES': 'You haven\'t save. Your changes will be lost.', - 'WILL_DELETE_CUSTOM_RESPONSE': 'The custom response will be deleted.' + 'WILL_DELETE_CUSTOM_RESPONSE': 'The custom response will be deleted.', + 'WILL_DELETE_DEPARTMENT': 'The department will be deleted. All the tickets will be transfer to the department selected.', + 'NO_STAFF_ASSIGNED': 'No staff member is assigned to this department.' }; diff --git a/client/src/reducers/config-reducer.js b/client/src/reducers/config-reducer.js index 13abe25d..3526bedf 100644 --- a/client/src/reducers/config-reducer.js +++ b/client/src/reducers/config-reducer.js @@ -14,7 +14,8 @@ class ConfigReducer extends Reducer { getTypeHandlers() { return { 'CHANGE_LANGUAGE': this.onLanguageChange, - 'INIT_CONFIGS_FULFILLED': this.onInitConfigs + 'INIT_CONFIGS_FULFILLED': this.onInitConfigs, + 'UPDATE_DATA_FULFILLED': this.onInitConfigs }; } @@ -29,7 +30,7 @@ class ConfigReducer extends Reducer { onInitConfigs(state, payload) { const currentLanguage = sessionStore.getItem('language'); - sessionStore.storeConfigs(_.extend(payload.data, { + sessionStore.storeConfigs(_.extend({}, payload.data, { language: currentLanguage || payload.language }));