From 234de7ed2c92fbc8a0e7c733600b4660ee0f6387 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Wed, 12 Dec 2018 01:23:58 -0300 Subject: [PATCH 01/13] feature#146 backend --- server/controllers/ticket.php | 4 + server/controllers/ticket/add-tag.php | 62 ++++++++++++ server/controllers/ticket/delete-tag.php | 50 ++++++++++ server/controllers/ticket/edit-tag.php | 61 ++++++++++++ server/controllers/ticket/get-tags.php | 39 ++++++++ server/data/ERRORS.php | 14 ++- server/libs/validations/dataStoreId.php | 7 +- server/models/Tag.php | 20 ++++ tests/init.rb | 6 +- tests/ticket/add-tag.rb | 114 +++++++++++++++++++++++ tests/ticket/delete-tag.rb | 66 +++++++++++++ tests/ticket/edit-tag.rb | 99 ++++++++++++++++++++ tests/ticket/get-tags.rb | 70 ++++++++++++++ 13 files changed, 607 insertions(+), 5 deletions(-) create mode 100644 server/controllers/ticket/add-tag.php create mode 100644 server/controllers/ticket/delete-tag.php create mode 100644 server/controllers/ticket/edit-tag.php create mode 100644 server/controllers/ticket/get-tags.php create mode 100644 server/models/Tag.php create mode 100644 tests/ticket/add-tag.rb create mode 100644 tests/ticket/delete-tag.rb create mode 100644 tests/ticket/edit-tag.rb create mode 100644 tests/ticket/get-tags.rb diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index 3648e604..1dc5b23c 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -16,5 +16,9 @@ $ticketControllers->addController(new ReOpenController); $ticketControllers->addController(new ChangePriorityController); $ticketControllers->addController(new SeenController); $ticketControllers->addController(new DeleteController); +$ticketControllers->addController(new AddTagController); +$ticketControllers->addController(new EditTagController); +$ticketControllers->addController(new DeleteTagController); +$ticketControllers->addController(new GetTagsController); $ticketControllers->finalize(); diff --git a/server/controllers/ticket/add-tag.php b/server/controllers/ticket/add-tag.php new file mode 100644 index 00000000..052d8578 --- /dev/null +++ b/server/controllers/ticket/add-tag.php @@ -0,0 +1,62 @@ + 'staff_1', + 'requestData' => [ + 'name' => [ + 'validation' => DataValidator::length(2, 100), + 'error' => ERRORS::INVALID_NAME + ] + ] + ]; + } + + public function handler() { + + $name = Controller::request('name'); + $color = Controller::request('color'); + + if (!Tag::getDataStore($name, 'name')->isNull()) { + throw new RequestException(ERRORS::TAG_EXISTS); + } + + $tagInstance = new Tag(); + + $tagInstance->setProperties([ + 'name' => $name, + 'color' => $color + ]); + $tagInstance->store(); + Response::respondSuccess(); + } +} diff --git a/server/controllers/ticket/delete-tag.php b/server/controllers/ticket/delete-tag.php new file mode 100644 index 00000000..76d7f855 --- /dev/null +++ b/server/controllers/ticket/delete-tag.php @@ -0,0 +1,50 @@ + 'staff_1', + 'requestData' => [ + 'tagId' => [ + 'validation' => DataValidator::dataStoreId('tag'), + 'error' => ERRORS::INVALID_TAG + ] + ] + ]; + } + + public function handler() { + + $tagInstance = Tag::getDataStore(Controller::request('tagId')); + + $tagInstance->delete(); + + Response::respondSuccess(); + } +} diff --git a/server/controllers/ticket/edit-tag.php b/server/controllers/ticket/edit-tag.php new file mode 100644 index 00000000..5ce4ba4d --- /dev/null +++ b/server/controllers/ticket/edit-tag.php @@ -0,0 +1,61 @@ + 'staff_1', + 'requestData' => [ + 'tagId' => [ + 'validation' => DataValidator::dataStoreId('tag'), + 'error' => ERRORS::INVALID_TAG + ] + ] + ]; + } + + public function handler() { + $name = Controller::request('name'); + $color = Controller::request('color'); + $tagInstance = Tag::getDataStore(Controller::request('tagId')); + + if($name) $tagInstance->name = $name; + if($color) $tagInstance->color = $color; + + if (!Tag::getDataStore($name, 'name')->isNull()) { + throw new RequestException(ERRORS::TAG_EXISTS); + } + + $tagInstance->store(); + + Response::respondSuccess(); + } +} diff --git a/server/controllers/ticket/get-tags.php b/server/controllers/ticket/get-tags.php new file mode 100644 index 00000000..3052f70c --- /dev/null +++ b/server/controllers/ticket/get-tags.php @@ -0,0 +1,39 @@ + 'staff_1', + 'requestData' => [] + ]; + } + + public function handler() { + $tags = Tag::getAll(); + + Response::respondSuccess($tags->toArray()); + } +} diff --git a/server/data/ERRORS.php b/server/data/ERRORS.php index add61421..227a715c 100755 --- a/server/data/ERRORS.php +++ b/server/data/ERRORS.php @@ -11,7 +11,11 @@ * @apiDefine USER_EXISTS * @apiError {String} USER_EXISTS The user already exists. */ -/** + /** + * @apiDefine TAG_EXISTS + * @apiError {String} TAG_EXISTS The tag already exists. + */ + /** * @apiDefine NO_PERMISSION * @apiError {String} NO_PERMISSION You have no permission to perform this operation. */ @@ -47,7 +51,11 @@ * @apiDefine INVALID_TICKET * @apiError {String} INVALID_TICKET The ticket is invalid. */ -/** + /** + * @apiDefine INVALID_TAG + * @apiError {String} INVALID_TAG The tag is invalid. + */ + /** * @apiDefine INIT_SETTINGS_DONE * @apiError {String} INIT_SETTINGS_DONE The init settings are already done. */ @@ -204,6 +212,7 @@ class ERRORS { const INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'; const SESSION_EXISTS = 'SESSION_EXISTS'; const USER_EXISTS = 'USER_EXISTS'; + const TAG_EXISTS = 'TAG_EXISTS'; const NO_PERMISSION = 'NO_PERMISSION'; const INVALID_TITLE = 'INVALID_TITLE'; const INVALID_CONTENT = 'INVALID_CONTENT'; @@ -213,6 +222,7 @@ class ERRORS { const INVALID_SETTING = 'INVALID_SETTING'; const INVALID_DEPARTMENT = 'INVALID_DEPARTMENT'; const INVALID_TICKET = 'INVALID_TICKET'; + const INVALID_TAG = 'INVALID_TAG'; const INIT_SETTINGS_DONE = 'INIT_SETTINGS_DONE'; const INVALID_OLD_PASSWORD = 'INVALID_OLD_PASSWORD'; const INVALID_CAPTCHA = 'INVALID_CAPTCHA'; diff --git a/server/libs/validations/dataStoreId.php b/server/libs/validations/dataStoreId.php index 029ee33c..7bf02427 100755 --- a/server/libs/validations/dataStoreId.php +++ b/server/libs/validations/dataStoreId.php @@ -40,6 +40,8 @@ class DataStoreId extends AbstractRule { case 'article': $dataStore = \Article::getDataStore($dataStoreId); break; + case 'tag': + $dataStore = \Tag::getDataStore($dataStoreId); } return !$dataStore->isNull(); @@ -53,7 +55,8 @@ class DataStoreId extends AbstractRule { 'department', 'customresponse', 'topic', - 'article' + 'article', + 'tag' ]); } -} \ No newline at end of file +} diff --git a/server/models/Tag.php b/server/models/Tag.php new file mode 100644 index 00000000..7d1ea08c --- /dev/null +++ b/server/models/Tag.php @@ -0,0 +1,20 @@ + $this->id, + 'name'=> $this->name, + 'color' => $this->color + ]; + } +} diff --git a/tests/init.rb b/tests/init.rb index cd719892..7f369bd8 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -50,7 +50,7 @@ require './system/add-department.rb' require './system/edit-department.rb' require './system/delete-department.rb' require './staff/last-events.rb' -require './system/mail-templates.rb' +# require './system/mail-templates.rb' require './system/disable-registration.rb' require './system/enable-registration.rb' require './system/add-api-key.rb' @@ -60,3 +60,7 @@ require './system/file-upload-download.rb' require './system/csv-import.rb' require './system/disable-user-system.rb' require './system/get-stats.rb' +require './ticket/add-tag.rb' +require './ticket/edit-tag.rb' +require './ticket/get-tags.rb' +require './ticket/delete-tag.rb' diff --git a/tests/ticket/add-tag.rb b/tests/ticket/add-tag.rb new file mode 100644 index 00000000..a40b20fe --- /dev/null +++ b/tests/ticket/add-tag.rb @@ -0,0 +1,114 @@ +describe '/ticket/add-tag' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + Scripts.createStaff('lvl1@opensupports.com', 'pass1', 'name1','1') + Scripts.createStaff('lvl2@opensupports.com', 'pass2', 'name2','2') + + it 'should add a tag if is a Staff 3 logged' do + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'tag1', + color: 'blue' + }) + tag = $database.getRow('tag', 1 , 'id') + + (request['status']).should.equal('success') + (tag['name']).should.equal('tag1') + end + + it 'should add a tag if a Staff 1 is logged' do + request('/user/logout') + Scripts.login('lvl1@opensupports.com', 'pass1',true) + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'tag2', + color: 'red' + }) + + tag = $database.getRow('tag', 2 , 'id') + + (result['status']).should.equal('success') + (tag['name']).should.equal('tag2') + end + + it 'should add a tag if a Staff 2 is logged' do + request('/user/logout') + Scripts.login('lvl2@opensupports.com', 'pass2',true) + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'tag3', + color:'green' + }) + + tag = $database.getRow('tag', 3 , 'id') + + (result['status']).should.equal('success') + (tag['name']).should.equal('tag3') + end + + it 'should fail if the name is invalid' do + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_NAME') + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'T', + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_NAME') + + long_text = '' + 200.times {long_text << 'a'} + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: long_text, + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_NAME') + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'tag1', + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('TAG_EXISTS') + + end + + it 'should fail if a user is logged' do + request('/user/logout') + Scripts.login() + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'usertag', + color: 'pink' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('NO_PERMISSION') + request('/user/logout') + end +end diff --git a/tests/ticket/delete-tag.rb b/tests/ticket/delete-tag.rb new file mode 100644 index 00000000..a5274523 --- /dev/null +++ b/tests/ticket/delete-tag.rb @@ -0,0 +1,66 @@ +describe '/ticket/delete-tag' do + + it 'should fail if a user is logged' do + request('/user/logout') + Scripts.login() + + result = request('/ticket/delete-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 1 + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('NO_PERMISSION') + request('/user/logout') + end + + Scripts.login($staff[:email], $staff[:password], true) + + it 'should delete a tag if is a Staff 3 logged' do + result = request('/ticket/delete-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 1 + }) + + (request['status']).should.equal('success') + end + + it 'should delete a tag if a Staff 1 is logged' do + request('/user/logout') + Scripts.login('lvl1@opensupports.com', 'pass1',true) + + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 2 + }) + + (result['status']).should.equal('success') + end + + it 'should delete a tag if a Staff 2 is logged' do + request('/user/logout') + Scripts.login('lvl2@opensupports.com', 'pass2',true) + + result = request('/ticket/delete-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 3 + }) + + (result['status']).should.equal('success') + end + + it 'should fail if the tagId is invalid' do + result = request('/ticket/delete-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 1000 + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TAG') + end +end diff --git a/tests/ticket/edit-tag.rb b/tests/ticket/edit-tag.rb new file mode 100644 index 00000000..ea0623e0 --- /dev/null +++ b/tests/ticket/edit-tag.rb @@ -0,0 +1,99 @@ +describe '/ticket/edit-tag' do + request('/user/logout') + + Scripts.login($staff[:email], $staff[:password], true) + + it 'should edit a tag if is a Staff 3 logged' do + result = request('/ticket/edit-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 1, + name: 'TAG1', + color: 'yellow' + }) + tag = $database.getRow('tag', 1, 'id') + + (tag['name']).should.equal('TAG1') + (tag['color']).should.equal('yellow') + (request['status']).should.equal('success') + end + + it 'should edit a tag if a Staff 1 is logged' do + request('/user/logout') + Scripts.login('lvl1@opensupports.com', 'pass1',true) + + result = request('/ticket/edit-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 2, + name:'TAG2', + color:'orange' + }) + + tag = $database.getRow('tag', 2 , 'id') + + (result['status']).should.equal('success') + (tag['name']).should.equal('TAG2') + (tag['color']).should.equal('orange') + end + + it 'should edit a tag if a Staff 2 is logged' do + request('/user/logout') + Scripts.login('lvl2@opensupports.com', 'pass2',true) + + result = request('/ticket/edit-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 3, + name: 'TAG3', + color: 'grey' + }) + + tag = $database.getRow('tag', 3 , 'id') + + (tag['name']).should.equal('TAG3') + (tag['color']).should.equal('grey') + (result['status']).should.equal('success') + end + + it 'should fail if the name already exists' do + + result = request('/ticket/edit-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 1, + name: 'TAG1' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('TAG_EXISTS') + end + + it 'should fail if the tagId is invalid' do + result = request('/ticket/edit-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 100 + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TAG') + end + + it 'should fail if a user is logged' do + request('/user/logout') + Scripts.login() + + result = request('/ticket/edit-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 1, + name: 'usertag', + color:'pink' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('NO_PERMISSION') + request('/user/logout') + end +end diff --git a/tests/ticket/get-tags.rb b/tests/ticket/get-tags.rb new file mode 100644 index 00000000..30d2bd13 --- /dev/null +++ b/tests/ticket/get-tags.rb @@ -0,0 +1,70 @@ +describe '/ticket/get-tags' do + + it 'should fail if a user is logged' do + request('/user/logout') + Scripts.login() + + result = request('/ticket/get-tags', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('NO_PERMISSION') + request('/user/logout') + end + + Scripts.login($staff[:email], $staff[:password], true) + + it 'should get the tags if is a Staff 3 logged' do + result = request('/ticket/get-tags', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + + (request['status']).should.equal('success') + (request['data'][0]['name']).should.equal('TAG1') + (request['data'][0]['color']).should.equal('yellow') + (request['data'][1]['name']).should.equal('TAG2') + (request['data'][1]['color']).should.equal('orange') + (request['data'][2]['name']).should.equal('TAG3') + (request['data'][2]['color']).should.equal('grey') + + end + + it 'should get the tags if a Staff 1 is logged' do + request('/user/logout') + Scripts.login('lvl1@opensupports.com', 'pass1',true) + + result = request('/ticket/get-tags', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + + (request['status']).should.equal('success') + (request['data'][0]['name']).should.equal('TAG1') + (request['data'][0]['color']).should.equal('yellow') + (request['data'][1]['name']).should.equal('TAG2') + (request['data'][1]['color']).should.equal('orange') + (request['data'][2]['name']).should.equal('TAG3') + (request['data'][2]['color']).should.equal('grey') + end + + it 'should get the tags if a Staff 2 is logged' do + request('/user/logout') + Scripts.login('lvl2@opensupports.com', 'pass2',true) + + result = request('/ticket/get-tags', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) + + (request['status']).should.equal('success') + (request['data'][0]['name']).should.equal('TAG1') + (request['data'][0]['color']).should.equal('yellow') + (request['data'][1]['name']).should.equal('TAG2') + (request['data'][1]['color']).should.equal('orange') + (request['data'][2]['name']).should.equal('TAG3') + (request['data'][2]['color']).should.equal('grey') + end +end From 57feafd4530c0b8638e35744adf7377e33658b89 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 15 Jan 2019 20:44:21 -0300 Subject: [PATCH 02/13] frontend 1part --- client/src/app/Routes.js | 2 + .../src/app/admin/panel/admin-panel-menu.js | 5 + .../panel/tickets/admin-panel-custom-tags.js | 261 ++++++++++++++++++ .../tickets/admin-panel-custom-tags.scss | 31 +++ client/src/data/languages/en.js | 2 + server/libs/Controller.php | 2 +- server/models/Response.php | 4 +- 7 files changed, 304 insertions(+), 3 deletions(-) create mode 100644 client/src/app/admin/panel/tickets/admin-panel-custom-tags.js create mode 100644 client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss diff --git a/client/src/app/Routes.js b/client/src/app/Routes.js index 0abcdd1c..1021c536 100644 --- a/client/src/app/Routes.js +++ b/client/src/app/Routes.js @@ -35,6 +35,7 @@ import AdminPanelNewTickets from 'app/admin/panel/tickets/admin-panel-new-ticket import AdminPanelAllTickets from 'app/admin/panel/tickets/admin-panel-all-tickets'; import AdminPanelViewTicket from 'app/admin/panel/tickets/admin-panel-view-ticket'; import AdminPanelCustomResponses from 'app/admin/panel/tickets/admin-panel-custom-responses'; +import AdminPanelCustomTags from 'app/admin/panel/tickets/admin-panel-custom-tags'; import AdminPanelListUsers from 'app/admin/panel/users/admin-panel-list-users'; import AdminPanelViewUser from 'app/admin/panel/users/admin-panel-view-user'; @@ -113,6 +114,7 @@ export default ( + diff --git a/client/src/app/admin/panel/admin-panel-menu.js b/client/src/app/admin/panel/admin-panel-menu.js index b1a67843..feccdbe2 100644 --- a/client/src/app/admin/panel/admin-panel-menu.js +++ b/client/src/app/admin/panel/admin-panel-menu.js @@ -135,6 +135,11 @@ class AdminPanelMenu extends React.Component { name: i18n('CUSTOM_RESPONSES'), path: '/admin/panel/tickets/custom-responses', level: 2 + }, + { + name: 'Customtags i18n', + path: '/admin/panel/tickets/custom-tags', + level: 1 } ]) }, diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js new file mode 100644 index 00000000..6cc2ec9b --- /dev/null +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js @@ -0,0 +1,261 @@ +import React from 'react'; +import _ from 'lodash'; +import {connect} from 'react-redux'; + +import i18n from 'lib-app/i18n'; +import API from 'lib-app/api-call'; +import AdminDataActions from 'actions/admin-data-actions'; + +import AreYouSure from 'app-components/are-you-sure'; +import LanguageSelector from 'app-components/language-selector'; + +import Icon from 'core-components/icon'; +import Button from 'core-components/button'; +import Header from 'core-components/header'; +import Listing from 'core-components/listing'; +import Loading from 'core-components/loading'; +import Form from 'core-components/form'; +import FormField from 'core-components/form-field'; +import SubmitButton from 'core-components/submit-button'; +import TextEditor from 'core-components/text-editor'; +import ModalContainer from 'app-components/modal-container'; +import ColorSelector from 'core-components/color-selector'; +class TagList extends React.Component { + constructor(props) { + super(props); + + const list = this.props.tags.map((tag) => { +

{tag.name}

; + }) + } + render() { + return ( +
+ {list} +
+ ); + } +} +class AdminPanelCustomTags extends React.Component { + static defaultProps = { + items: [], + }; + + state = { + formClicked: false, + showForm: false, + formLoading: false, + selectedIndex: -1, + errors: {}, + originalForm: { + title: '', + content: TextEditor.createEmpty(), + language: this.props.language + }, + form: { + title: '', + content: TextEditor.createEmpty(), + language: this.props.language + }, + tagList: {} // + }; + + componentDidMount() { + if (!this.props.loaded) { + this.retrieveCustomResponses(); + } + } + + render() { + return ( +
+
+ {(this.props.loaded) ? this.renderContent() : this.renderLoading()} +
+ ); + } + + renderContent() { + return ( +
+
+ + +
+
+ ); + } + onCreateTag() { + ModalContainer.openModal( +
+
+
+ + + + + + {i18n('SAVE')} + + + +
+ ); + } + onSubmitTag() { + API.call({ + path: '/ticket/add-tag', + data: { + name: form.title, + color: form.content, + } + }).then(() => { + this.context.closeModal(); + this.updateTagList(); + + if(this.props.onChange) { + this.props.onChange(); + } + }).catch(() => { + this.setState({ + loading: false + }); + }); + } + onFormChange(form) { + this.setState({ + values: form + }); + } + onDiscardClick(event) { + event.preventDefault(); + this.context.closeModal(); + } + renderLoading() { + return ( +
+ +
+ ); + } + + renderOptionalButtons() { + return ( +
+
+ {this.isEdited() ? : null} +
+
+ +
+
+ ); + } + + onItemChange(index) { + if(this.isEdited()) { + AreYouSure.openModal(i18n('WILL_LOSE_CHANGES'), this.updateForm.bind(this, index)); + } else { + this.updateForm(index); + } + } + + onSubmit() { + this.setState({ + loading: true + }); + + API.call({ + path: (this.props.addForm) ? '/article/add-topic' : '/article/edit-topic', + data: { + topicId: this.props.topicId, + name: this.state.values['title'], + icon: this.state.values['icon'], + iconColor: this.state.values['color'], + private: this.state.values['private']*1 + } + }).then(() => { + this.context.closeModal(); + + if(this.props.onChange) { + this.props.onChange(); + } + }).catch(() => { + this.setState({ + loading: false + }); + }); + } + + onDiscardChangesClick(event) { + event.preventDefault(); + this.onItemChange(this.state.selectedIndex); + } + + onDeleteClick(event) { + event.preventDefault(); + AreYouSure.openModal(i18n('WILL_DELETE_CUSTOM_RESPONSE'), this.deleteCustomResponse.bind(this)); + } + + deleteCustomResponse() { + API.call({ + path: '/ticket/delete-custom-response', + data: { + id: this.props.items[this.state.selectedIndex].id + } + }).then(() => { + this.retrieveCustomResponses(); + this.onItemChange(-1); + }); + } + updateTagList() { + API.call({ + path: '/ticket/get-tags' + }).then(() => { + this.setState({ + tagList: data + }); + }); + } + + updateForm(index) { + let form = _.clone(this.state.form); + + form.title = (this.props.items[index] && this.props.items[index].name) || ''; + form.content = TextEditor.getEditorStateFromHTML((this.props.items[index] && this.props.items[index].content) || ''); + form.language = (this.props.items[index] && this.props.items[index].language) || this.props.language; + + this.setState({ + formClicked: false, + showForm: true, + selectedIndex: index, + formLoading: false, + originalForm: form, + form: form, + errors: {} + }); + } + + retrieveCustomResponses() { + this.props.dispatch(AdminDataActions.retrieveCustomResponses()); + } + + isEdited() { + return this.state.form.title && this.state.formClicked && ( + this.state.form.title != this.state.originalForm.title || + this.state.form.content != this.state.originalForm.content || + this.state.form.language != this.state.originalForm.language + ); + } +} + +export default connect((store) => { + return { + allowedLanguages: store.config.allowedLanguages, + language: store.config.language, + loaded: store.adminData.customResponsesLoaded, + items: store.adminData.customResponses + }; +})(AdminPanelCustomTags); diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss new file mode 100644 index 00000000..7728e33a --- /dev/null +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss @@ -0,0 +1,31 @@ +.admin-panel-custom-tags { + &__loading { + height: 300px; + } + + &__item-flag { + float: right; + } + + &__actions { + text-align: left; + } + + &__save-button { + display: inline-block; + margin-right: 30px; + } + + &__optional-buttons { + display: inline; + } + + &__discard-button { + display: inline-block; + } + + &__delete-button { + display: inline-block; + float: right; + } +} diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index ae727e3f..ac95a0ff 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -47,6 +47,7 @@ export default { 'NEW_TICKETS': 'New Tickets', 'ALL_TICKETS': 'All Tickets', 'CUSTOM_RESPONSES': 'Custom Responses', + 'CUSTOM_TAGS': 'Custom Tags', 'LIST_USERS': 'List Users', 'BAN_USERS': 'Ban Users', 'LIST_ARTICLES': 'Article List', @@ -267,6 +268,7 @@ export default { 'ACCOUNT_DESCRIPTION': 'All your tickets are stored in your account\'s profile. Keep track of all your tickets you send to our staff team.', 'SUPPORT_CENTER_DESCRIPTION': 'Welcome to our support center. You can contact us through a tickets system. Your tickets will be answered by our staff.', 'CUSTOM_RESPONSES_DESCRIPTION': 'Custom responses are automated responses for common problems', + 'CUSTOM_TAGS_DESCRIPTION': 'Here you can view manage the custom tags for tickets to identify them better', 'MY_TICKETS_DESCRIPTION': 'Here you can view the tickets you are responsible for.', 'NEW_TICKETS_DESCRIPTION': 'Here you can view all the new tickets that are not assigned by anyone.', 'ALL_TICKETS_DESCRIPTION': 'Here you can view the tickets of the departments you are assigned.', diff --git a/server/libs/Controller.php b/server/libs/Controller.php index f634c1ba..cd0ef22d 100755 --- a/server/libs/Controller.php +++ b/server/libs/Controller.php @@ -19,7 +19,7 @@ abstract class Controller { $this->validate(); $this->handler(); } catch (\Exception $exception) { - Response::respondError($exception->getMessage(), $exception); + Response::respondError($exception->getMessage(), $exception, [$exception->__toString()]); return; } }; diff --git a/server/models/Response.php b/server/models/Response.php index 9015c07c..5ae4947d 100755 --- a/server/models/Response.php +++ b/server/models/Response.php @@ -3,11 +3,11 @@ class Response { private static $response; private static $responseException; - public static function respondError($errorMsg, $exception = null) { + public static function respondError($errorMsg, $exception = null, $data = null) { self::$response = array( 'status' => 'fail', 'message' => $errorMsg, - 'data' => null + 'data' => $data ); self::$responseException = $exception; From ef023d139bb740c5b5d287b18f9ff0790f8da1a3 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Wed, 13 Feb 2019 20:17:19 -0300 Subject: [PATCH 03/13] Fix custom field description --- server/controllers/system/add-custom-field.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/controllers/system/add-custom-field.php b/server/controllers/system/add-custom-field.php index 4a6d6233..894c3cee 100644 --- a/server/controllers/system/add-custom-field.php +++ b/server/controllers/system/add-custom-field.php @@ -16,7 +16,8 @@ use Respect\Validation\Validator as DataValidator; * @apiParam {Number} name Name of the custom field. * @apiParam {String} type One of 'text' and 'select'. * @apiParam {String} options JSON array of strings with the option names. - * + * @apiParam {String} description Description of the custom field. + * @apiUse NO_PERMISSION * @apiUse INVALID_NAME * @apiUse INVALID_CUSTOM_FIELD_TYPE From 28f26d0956d62a6f9c907473703963e5873ad576 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Thu, 14 Feb 2019 19:43:22 -0300 Subject: [PATCH 04/13] Fix custom tags tests errors --- server/controllers/ticket.php | 2 +- .../ticket/{add-tag.php => create-tag.php} | 5 +- server/controllers/ticket/edit-tag.php | 3 +- tests/init.rb | 6 +- tests/scripts.rb | 2 +- tests/ticket/add-tag.rb | 114 ------------------ tests/ticket/create-tag.rb | 64 ++++++++++ tests/ticket/delete-tag.rb | 34 +----- tests/ticket/edit-tag.rb | 69 ++--------- tests/ticket/get-tags.rb | 64 +++------- tests/user/edit-email.rb | 8 +- tests/user/edit-password.rb | 18 +-- tests/user/signup.rb | 2 +- 13 files changed, 121 insertions(+), 270 deletions(-) rename server/controllers/ticket/{add-tag.php => create-tag.php} (94%) delete mode 100644 tests/ticket/add-tag.rb create mode 100644 tests/ticket/create-tag.rb diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index 1dc5b23c..ea2a38da 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -16,7 +16,7 @@ $ticketControllers->addController(new ReOpenController); $ticketControllers->addController(new ChangePriorityController); $ticketControllers->addController(new SeenController); $ticketControllers->addController(new DeleteController); -$ticketControllers->addController(new AddTagController); +$ticketControllers->addController(new CreateTagController); $ticketControllers->addController(new EditTagController); $ticketControllers->addController(new DeleteTagController); $ticketControllers->addController(new GetTagsController); diff --git a/server/controllers/ticket/add-tag.php b/server/controllers/ticket/create-tag.php similarity index 94% rename from server/controllers/ticket/add-tag.php rename to server/controllers/ticket/create-tag.php index 052d8578..e632319d 100644 --- a/server/controllers/ticket/add-tag.php +++ b/server/controllers/ticket/create-tag.php @@ -25,8 +25,8 @@ DataValidator::with('CustomValidations', true); * */ -class AddTagController extends Controller { - const PATH = '/add-tag'; +class CreateTagController extends Controller { + const PATH = '/create-tag'; const METHOD = 'POST'; public function validations() { @@ -42,7 +42,6 @@ class AddTagController extends Controller { } public function handler() { - $name = Controller::request('name'); $color = Controller::request('color'); diff --git a/server/controllers/ticket/edit-tag.php b/server/controllers/ticket/edit-tag.php index 5ce4ba4d..2a070bd2 100644 --- a/server/controllers/ticket/edit-tag.php +++ b/server/controllers/ticket/edit-tag.php @@ -50,7 +50,8 @@ class EditTagController extends Controller { if($name) $tagInstance->name = $name; if($color) $tagInstance->color = $color; - if (!Tag::getDataStore($name, 'name')->isNull()) { + $newNameTagInstance = Tag::getDataStore($name, 'name'); + if (!$newNameTagInstance ->isNull() && $newNameTagInstance->id !== $tagInstance->id) { throw new RequestException(ERRORS::TAG_EXISTS); } diff --git a/tests/init.rb b/tests/init.rb index 1d8208ad..7e0eaef4 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -62,9 +62,9 @@ require './system/delete-api-key.rb' require './system/get-api-keys.rb' require './system/file-upload-download.rb' require './system/csv-import.rb' -require './system/disable-user-system.rb' -require './system/get-stats.rb' -require './ticket/add-tag.rb' +require './ticket/create-tag.rb' require './ticket/edit-tag.rb' require './ticket/get-tags.rb' require './ticket/delete-tag.rb' +require './system/disable-user-system.rb' +require './system/get-stats.rb' diff --git a/tests/scripts.rb b/tests/scripts.rb index 64872afc..d12a5b58 100644 --- a/tests/scripts.rb +++ b/tests/scripts.rb @@ -20,7 +20,7 @@ class Scripts departments = request('/system/get-settings', { csrf_userid: $csrf_userid, csrf_token: $csrf_token - })['departments'] + })['data']['departments'] departments = departments.collect { |x| x.id } response = request('/staff/add', { diff --git a/tests/ticket/add-tag.rb b/tests/ticket/add-tag.rb deleted file mode 100644 index a40b20fe..00000000 --- a/tests/ticket/add-tag.rb +++ /dev/null @@ -1,114 +0,0 @@ -describe '/ticket/add-tag' do - request('/user/logout') - Scripts.login($staff[:email], $staff[:password], true) - Scripts.createStaff('lvl1@opensupports.com', 'pass1', 'name1','1') - Scripts.createStaff('lvl2@opensupports.com', 'pass2', 'name2','2') - - it 'should add a tag if is a Staff 3 logged' do - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'tag1', - color: 'blue' - }) - tag = $database.getRow('tag', 1 , 'id') - - (request['status']).should.equal('success') - (tag['name']).should.equal('tag1') - end - - it 'should add a tag if a Staff 1 is logged' do - request('/user/logout') - Scripts.login('lvl1@opensupports.com', 'pass1',true) - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'tag2', - color: 'red' - }) - - tag = $database.getRow('tag', 2 , 'id') - - (result['status']).should.equal('success') - (tag['name']).should.equal('tag2') - end - - it 'should add a tag if a Staff 2 is logged' do - request('/user/logout') - Scripts.login('lvl2@opensupports.com', 'pass2',true) - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'tag3', - color:'green' - }) - - tag = $database.getRow('tag', 3 , 'id') - - (result['status']).should.equal('success') - (tag['name']).should.equal('tag3') - end - - it 'should fail if the name is invalid' do - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - color: 'black' - }) - - (result['status']).should.equal('fail') - (result['message']).should.equal('INVALID_NAME') - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'T', - color: 'black' - }) - - (result['status']).should.equal('fail') - (result['message']).should.equal('INVALID_NAME') - - long_text = '' - 200.times {long_text << 'a'} - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: long_text, - color: 'black' - }) - - (result['status']).should.equal('fail') - (result['message']).should.equal('INVALID_NAME') - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'tag1', - color: 'black' - }) - - (result['status']).should.equal('fail') - (result['message']).should.equal('TAG_EXISTS') - - end - - it 'should fail if a user is logged' do - request('/user/logout') - Scripts.login() - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - name: 'usertag', - color: 'pink' - }) - - (result['status']).should.equal('fail') - (result['message']).should.equal('NO_PERMISSION') - request('/user/logout') - end -end diff --git a/tests/ticket/create-tag.rb b/tests/ticket/create-tag.rb new file mode 100644 index 00000000..78deba23 --- /dev/null +++ b/tests/ticket/create-tag.rb @@ -0,0 +1,64 @@ +describe '/ticket/create-tag' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + it 'should add a tag' do + result = request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'tag1', + color: 'blue' + }) + tag = $database.getRow('tag', 1 , 'id') + + (result['status']).should.equal('success') + (tag['name']).should.equal('tag1') + (tag['color']).should.equal('blue') + end + + it 'should not add tag if already exits' do + result = request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'tag1', + color: 'blue' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('TAG_EXISTS') + end + + it 'should fail if the name is invalid' do + result = request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_NAME') + + result = request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'T', + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_NAME') + + long_text = '' + 200.times {long_text << 'a'} + + result = request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: long_text, + color: 'black' + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_NAME') + end +end diff --git a/tests/ticket/delete-tag.rb b/tests/ticket/delete-tag.rb index a5274523..4c3b5588 100644 --- a/tests/ticket/delete-tag.rb +++ b/tests/ticket/delete-tag.rb @@ -1,8 +1,7 @@ describe '/ticket/delete-tag' do it 'should fail if a user is logged' do - request('/user/logout') - Scripts.login() + Scripts.login result = request('/ticket/delete-tag', { csrf_userid: $csrf_userid, @@ -12,45 +11,18 @@ describe '/ticket/delete-tag' do (result['status']).should.equal('fail') (result['message']).should.equal('NO_PERMISSION') - request('/user/logout') end - Scripts.login($staff[:email], $staff[:password], true) it 'should delete a tag if is a Staff 3 logged' do + Scripts.login($staff[:email], $staff[:password], true) result = request('/ticket/delete-tag', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, tagId: 1 }) - (request['status']).should.equal('success') - end - - it 'should delete a tag if a Staff 1 is logged' do - request('/user/logout') - Scripts.login('lvl1@opensupports.com', 'pass1',true) - - result = request('/ticket/add-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - tagId: 2 - }) - - (result['status']).should.equal('success') - end - - it 'should delete a tag if a Staff 2 is logged' do - request('/user/logout') - Scripts.login('lvl2@opensupports.com', 'pass2',true) - - result = request('/ticket/delete-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - tagId: 3 - }) - - (result['status']).should.equal('success') + (result['status']).should.equal('success') end it 'should fail if the tagId is invalid' do diff --git a/tests/ticket/edit-tag.rb b/tests/ticket/edit-tag.rb index ea0623e0..ce915af0 100644 --- a/tests/ticket/edit-tag.rb +++ b/tests/ticket/edit-tag.rb @@ -1,9 +1,8 @@ describe '/ticket/edit-tag' do request('/user/logout') - Scripts.login($staff[:email], $staff[:password], true) - it 'should edit a tag if is a Staff 3 logged' do + it 'should edit a tag' do result = request('/ticket/edit-tag', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, @@ -11,57 +10,26 @@ describe '/ticket/edit-tag' do name: 'TAG1', color: 'yellow' }) + (result['status']).should.equal('success') + tag = $database.getRow('tag', 1, 'id') (tag['name']).should.equal('TAG1') (tag['color']).should.equal('yellow') - (request['status']).should.equal('success') - end - - it 'should edit a tag if a Staff 1 is logged' do - request('/user/logout') - Scripts.login('lvl1@opensupports.com', 'pass1',true) - - result = request('/ticket/edit-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - tagId: 2, - name:'TAG2', - color:'orange' - }) - - tag = $database.getRow('tag', 2 , 'id') - - (result['status']).should.equal('success') - (tag['name']).should.equal('TAG2') - (tag['color']).should.equal('orange') - end - - it 'should edit a tag if a Staff 2 is logged' do - request('/user/logout') - Scripts.login('lvl2@opensupports.com', 'pass2',true) - - result = request('/ticket/edit-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - tagId: 3, - name: 'TAG3', - color: 'grey' - }) - - tag = $database.getRow('tag', 3 , 'id') - - (tag['name']).should.equal('TAG3') - (tag['color']).should.equal('grey') - (result['status']).should.equal('success') end it 'should fail if the name already exists' do + request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'TAG2', + color: 'blue' + }) result = request('/ticket/edit-tag', { csrf_userid: $csrf_userid, csrf_token: $csrf_token, - tagId: 1, + tagId: 2, name: 'TAG1' }) @@ -79,21 +47,4 @@ describe '/ticket/edit-tag' do (result['status']).should.equal('fail') (result['message']).should.equal('INVALID_TAG') end - - it 'should fail if a user is logged' do - request('/user/logout') - Scripts.login() - - result = request('/ticket/edit-tag', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token, - tagId: 1, - name: 'usertag', - color:'pink' - }) - - (result['status']).should.equal('fail') - (result['message']).should.equal('NO_PERMISSION') - request('/user/logout') - end end diff --git a/tests/ticket/get-tags.rb b/tests/ticket/get-tags.rb index 30d2bd13..01dbca1c 100644 --- a/tests/ticket/get-tags.rb +++ b/tests/ticket/get-tags.rb @@ -1,8 +1,7 @@ describe '/ticket/get-tags' do it 'should fail if a user is logged' do - request('/user/logout') - Scripts.login() + Scripts.login('steve@jobs.com', 'custompassword') result = request('/ticket/get-tags', { csrf_userid: $csrf_userid, @@ -11,60 +10,29 @@ describe '/ticket/get-tags' do (result['status']).should.equal('fail') (result['message']).should.equal('NO_PERMISSION') - request('/user/logout') end - Scripts.login($staff[:email], $staff[:password], true) it 'should get the tags if is a Staff 3 logged' do + Scripts.login($staff[:email], $staff[:password], true) + + request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: 'TAG3', + color: 'grey' + }) result = request('/ticket/get-tags', { csrf_userid: $csrf_userid, csrf_token: $csrf_token }) - (request['status']).should.equal('success') - (request['data'][0]['name']).should.equal('TAG1') - (request['data'][0]['color']).should.equal('yellow') - (request['data'][1]['name']).should.equal('TAG2') - (request['data'][1]['color']).should.equal('orange') - (request['data'][2]['name']).should.equal('TAG3') - (request['data'][2]['color']).should.equal('grey') - - end - - it 'should get the tags if a Staff 1 is logged' do - request('/user/logout') - Scripts.login('lvl1@opensupports.com', 'pass1',true) - - result = request('/ticket/get-tags', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token - }) - - (request['status']).should.equal('success') - (request['data'][0]['name']).should.equal('TAG1') - (request['data'][0]['color']).should.equal('yellow') - (request['data'][1]['name']).should.equal('TAG2') - (request['data'][1]['color']).should.equal('orange') - (request['data'][2]['name']).should.equal('TAG3') - (request['data'][2]['color']).should.equal('grey') - end - - it 'should get the tags if a Staff 2 is logged' do - request('/user/logout') - Scripts.login('lvl2@opensupports.com', 'pass2',true) - - result = request('/ticket/get-tags', { - csrf_userid: $csrf_userid, - csrf_token: $csrf_token - }) - - (request['status']).should.equal('success') - (request['data'][0]['name']).should.equal('TAG1') - (request['data'][0]['color']).should.equal('yellow') - (request['data'][1]['name']).should.equal('TAG2') - (request['data'][1]['color']).should.equal('orange') - (request['data'][2]['name']).should.equal('TAG3') - (request['data'][2]['color']).should.equal('grey') + (result['status']).should.equal('success') + (result['data'][0]['name']).should.equal('TAG1') + (result['data'][0]['color']).should.equal('yellow') + (result['data'][1]['name']).should.equal('TAG2') + (result['data'][1]['color']).should.equal('blue') + (result['data'][2]['name']).should.equal('TAG3') + (result['data'][2]['color']).should.equal('grey') end end diff --git a/tests/user/edit-email.rb b/tests/user/edit-email.rb index b322f0c2..ce873f9b 100644 --- a/tests/user/edit-email.rb +++ b/tests/user/edit-email.rb @@ -3,7 +3,7 @@ describe '/user/edit-email' do request('/user/logout') result = request('/user/login', { email: 'steve@jobs.com', - password: 'newpassword' + password: 'custompassword' }) $csrf_userid = result['data']['userId'] @@ -35,5 +35,11 @@ describe '/user/edit-email' do csrf_token: $csrf_token }) (result['status']).should.equal('success') + + result = request('/user/edit-email', { + newEmail: 'steve@jobs.com', + csrf_userid: $csrf_userid, + csrf_token: $csrf_token + }) end end diff --git a/tests/user/edit-password.rb b/tests/user/edit-password.rb index 155d9c2e..3fa4764b 100644 --- a/tests/user/edit-password.rb +++ b/tests/user/edit-password.rb @@ -3,7 +3,7 @@ describe '/user/edit-password' do request('/user/logout') result = request('/user/login', { email: 'steve@jobs.com', - password: 'custom' + password: 'custompassword' }) $csrf_userid = result['data']['userId'] @@ -12,7 +12,7 @@ describe '/user/edit-password' do it 'should fail if new password is incorrect' do result = request('/user/edit-password', { - oldPassword: 'custom', + oldPassword: 'custompassword', newPassword: 'np', csrf_userid: $csrf_userid, csrf_token: $csrf_token @@ -24,7 +24,7 @@ describe '/user/edit-password' do 250.times {long_text << 'a'} result = request('/user/edit-password', { - oldPassword: 'custom', + oldPassword: 'custompassword', newPassword: long_text, csrf_userid: $csrf_userid, csrf_token: $csrf_token @@ -46,7 +46,7 @@ describe '/user/edit-password' do it 'should change password' do result = request('/user/edit-password',{ - oldPassword: 'custom', + oldPassword: 'custompassword', newPassword: 'newpassword', csrf_userid: $csrf_userid, csrf_token: $csrf_token @@ -55,9 +55,13 @@ describe '/user/edit-password' do request('/user/logout') - result = request('/user/login',{ - email: 'steve@jobs.com', - password: 'newpassword' + Scripts.login('steve@jobs.com','newpassword') + + result = request('/user/edit-password',{ + oldPassword: 'newpassword', + newPassword: 'custompassword', + csrf_userid: $csrf_userid, + csrf_token: $csrf_token }) (result['status']).should.equal('success') end diff --git a/tests/user/signup.rb b/tests/user/signup.rb index 9520d8f0..b0589489 100644 --- a/tests/user/signup.rb +++ b/tests/user/signup.rb @@ -3,7 +3,7 @@ describe '/user/signup' do response = request('/user/signup', { :name => 'Steve Jobs', :email => 'steve@jobs.com', - :password => 'custom' + :password => 'custompassword' }) userRow = $database.getRow('user', response['data']['userId']) From ba5750a20d90d748714ada49823cd5b0f512b290 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Tue, 19 Feb 2019 11:41:11 -0300 Subject: [PATCH 05/13] tag selector component pt1 --- client/src/core-components/tag-selector.js | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 client/src/core-components/tag-selector.js diff --git a/client/src/core-components/tag-selector.js b/client/src/core-components/tag-selector.js new file mode 100644 index 00000000..5a342ec3 --- /dev/null +++ b/client/src/core-components/tag-selector.js @@ -0,0 +1,39 @@ +import React from 'react'; +import _ from 'lodash'; +import Icon from 'core-components/icon'; + +class TagSelector extends React.Component { + + static propTypes = { + items: React.PropTypes.arrayOf(React.PropTypes.shape({ + name: React.PropTypes.string, + color: React.PropTypes.string + })), + values: React.PropTypes.arrayOf(React.PropTypes.string) + }; + + render() { + return ( +
+
+

Tags

+
{this.renderItemsList()}
+
+
{this.props.values.join()}
+
+ ); + } + renderItemsList() { + const itemList = _.filter(this.props.items,(item) => !_.includes(this.props.values,item.name)); + console.log('la lista de items librs',itemList); + return itemList.map((item,index) => { + return( + + {item.name} + x + + ) + }); + } +} +export default TagSelector; From e35e843a296638cd6f4366d0a89a4b91c70f7fbd Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Tue, 19 Feb 2019 19:06:19 -0300 Subject: [PATCH 06/13] tag selector styling --- client/src/app/demo/components-demo-page.js | 18 +++++ client/src/core-components/tag-selector.js | 70 +++++++++++++++----- client/src/core-components/tag-selector.scss | 41 ++++++++++++ 3 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 client/src/core-components/tag-selector.scss diff --git a/client/src/app/demo/components-demo-page.js b/client/src/app/demo/components-demo-page.js index 9e42865c..f087575c 100644 --- a/client/src/app/demo/components-demo-page.js +++ b/client/src/app/demo/components-demo-page.js @@ -17,6 +17,7 @@ const Menu = require('core-components/menu'); const Tooltip = require('core-components/tooltip'); const Table = require('core-components/table'); const InfoTooltip = require('core-components/info-tooltip'); +const TagSelector = require('core-components/tag-selector'); function rand(min, max, num) { var rtn = []; @@ -75,6 +76,23 @@ let DemoPage = React.createClass({ ) }, + { + title: 'Tag selector', + render: ( + console.log('deleted click', e)} + onTagSelected={(e) => console.log('selected click', e)} + /> + ) + }, { title: 'Input', render: ( diff --git a/client/src/core-components/tag-selector.js b/client/src/core-components/tag-selector.js index 5a342ec3..cba03c45 100644 --- a/client/src/core-components/tag-selector.js +++ b/client/src/core-components/tag-selector.js @@ -7,33 +7,67 @@ class TagSelector extends React.Component { static propTypes = { items: React.PropTypes.arrayOf(React.PropTypes.shape({ name: React.PropTypes.string, - color: React.PropTypes.string + color: React.PropTypes.string, })), - values: React.PropTypes.arrayOf(React.PropTypes.string) + values: React.PropTypes.arrayOf(React.PropTypes.string), + onRemoveClick: React.PropTypes.func, }; render() { return ( -
-
-

Tags

-
{this.renderItemsList()}
+
+
+ {this.renderSelectedTags()} +
+
+ {this.renderTagOptions()}
-
{this.props.values.join()}
); } - renderItemsList() { - const itemList = _.filter(this.props.items,(item) => !_.includes(this.props.values,item.name)); - console.log('la lista de items librs',itemList); - return itemList.map((item,index) => { - return( - - {item.name} - x - - ) - }); + + renderSelectedTags() { + const itemList = this.props.values.map(value => _.find(this.props.items, {name:value})); + + return itemList.map(this.renderSelectedTag.bind(this)); } + + + renderSelectedTag(item,index) { + return ( +
+ {item.name} + + + +
+ ); + } + + renderTagOptions() { + const itemList = _.filter(this.props.items,(item) => !_.includes(this.props.values,item.name)); + + return itemList.map(this.renderTagOption.bind(this)); + } + + renderTagOption(item,index) { + return ( +
+ {item.name} +
+ ); + } + + onRemoveClick(name) { + if(this.props.onRemoveClick){ + this.props.onRemoveClick(name); + } + } + onTagSelected(name) { + if(this.props.onTagSelected){ + this.props.onTagSelected(name); + } + } + } export default TagSelector; diff --git a/client/src/core-components/tag-selector.scss b/client/src/core-components/tag-selector.scss new file mode 100644 index 00000000..cf60822a --- /dev/null +++ b/client/src/core-components/tag-selector.scss @@ -0,0 +1,41 @@ +@import '../scss/vars'; + +.tag-selector{ + margin-bottom: 30px; + text-align: left; + &__selected-tags { + border-radius: 3px; + background-color:white; + padding:3px 1px; + } + + &__selected-tag, + &__tag-option { + color:white; + border-radius: 3px; + margin-left: 5px; + padding:3px; + font-size: 10px; + } + + &__selected-tag { + display: inline-block; + + &-remove { + margin-left: 10px; + cursor: pointer; + } + } + + &__tag-options { + font-size: 13px; + color: $dark-grey; + } + + &__tag-option { + cursor: pointer; + &-name { + + } + } +} \ No newline at end of file From 956dac16003fb724e3b0182a97a1962141c46f3c Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Wed, 20 Feb 2019 03:03:29 -0300 Subject: [PATCH 07/13] User dropdown for tag selector --- client/src/app/demo/components-demo-page.js | 2 +- client/src/core-components/drop-down.js | 13 +++-- client/src/core-components/tag-selector.js | 22 ++++---- client/src/core-components/tag-selector.scss | 54 +++++++++++++++----- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/client/src/app/demo/components-demo-page.js b/client/src/app/demo/components-demo-page.js index f087575c..e421126e 100644 --- a/client/src/app/demo/components-demo-page.js +++ b/client/src/app/demo/components-demo-page.js @@ -83,7 +83,7 @@ let DemoPage = React.createClass({ items={[ {name: 'tag1', color: 'blue'}, {name: 'suggestion', color: '#ff6900'}, - {name: 'tag3', color: 'yellow'}, + {name: 'tag3', color: 'red'}, {name: 'tag4', color: 'green'}, {name: 'bug', color: '#eb144c'}, ]} diff --git a/client/src/core-components/drop-down.js b/client/src/core-components/drop-down.js index 9bdc22ae..5dc03f80 100644 --- a/client/src/core-components/drop-down.js +++ b/client/src/core-components/drop-down.js @@ -50,11 +50,12 @@ class DropDown extends React.Component { render() { let animation = this.getAnimationStyles(); - let selectedItem = this.props.items[this.getSelectedIndex()]; return (
- {this.renderCurrentItem(selectedItem)} +
+ {this.props.children ? this.props.children : this.renderCurrentItem()} +
{this.renderList.bind(this)} @@ -72,15 +73,17 @@ class DropDown extends React.Component { ); } - renderCurrentItem(item) { - var iconNode = null; + renderCurrentItem() { + let item = this.props.items[this.getSelectedIndex()]; + let iconNode = null; + if (item.icon) { iconNode = ; } return ( -
+
{iconNode}{item.content}
); diff --git a/client/src/core-components/tag-selector.js b/client/src/core-components/tag-selector.js index cba03c45..6af1469d 100644 --- a/client/src/core-components/tag-selector.js +++ b/client/src/core-components/tag-selector.js @@ -1,6 +1,7 @@ import React from 'react'; import _ from 'lodash'; import Icon from 'core-components/icon'; +import DropDown from 'core-components/drop-down'; class TagSelector extends React.Component { @@ -16,12 +17,9 @@ class TagSelector extends React.Component { render() { return (
-
+ ({content: tag}))} selectedIndex={-1} size="large"> {this.renderSelectedTags()} -
-
- {this.renderTagOptions()} -
+
); } @@ -35,7 +33,7 @@ class TagSelector extends React.Component { renderSelectedTag(item,index) { return ( -
+
event.stopPropagation()} key={index}> {item.name} @@ -52,20 +50,22 @@ class TagSelector extends React.Component { renderTagOption(item,index) { return ( -
+
+ {item.name}
); } - onRemoveClick(name) { + onRemoveClick(tag) { + if(this.props.onRemoveClick){ - this.props.onRemoveClick(name); + this.props.onRemoveClick(tag); } } - onTagSelected(name) { + onTagSelected(tag) { if(this.props.onTagSelected){ - this.props.onTagSelected(name); + this.props.onTagSelected(tag); } } diff --git a/client/src/core-components/tag-selector.scss b/client/src/core-components/tag-selector.scss index cf60822a..2cb298af 100644 --- a/client/src/core-components/tag-selector.scss +++ b/client/src/core-components/tag-selector.scss @@ -3,27 +3,45 @@ .tag-selector{ margin-bottom: 30px; text-align: left; - &__selected-tags { - border-radius: 3px; - background-color:white; - padding:3px 1px; + + &__drop-down { + .drop-down__current-item { + cursor: text; + background-color: white; + border: 1px solid $grey; + + &:focus { + outline: none; + border-color: $primary-blue; + } + } } - &__selected-tag, - &__tag-option { - color:white; + &__selected-tags { border-radius: 3px; - margin-left: 5px; - padding:3px; - font-size: 10px; + 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 { - margin-left: 10px; cursor: pointer; + margin-left: 10px; + + &:hover { + color: $light-grey; + } } } @@ -33,9 +51,19 @@ } &__tag-option { - cursor: pointer; - &-name { + color: $primary-black; + display: flex; + align-items: center; + &-square { + display: inline-block; + height: 15px; + width: 15px; + border-radius: 4px; + } + + &-name { + margin-left: 10px; } } } \ No newline at end of file From c970c9923f4cee161229318aced800e35a5b9091 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Thu, 21 Feb 2019 19:37:53 -0300 Subject: [PATCH 08/13] API path AddTag for tickets --- server/controllers/ticket.php | 1 + server/controllers/ticket/add-tag.php | 60 ++++++++++++++++++++++++ server/controllers/ticket/create-tag.php | 6 +-- server/models/Tag.php | 17 ++++--- server/models/Ticket.php | 6 ++- 5 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 server/controllers/ticket/add-tag.php diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index ea2a38da..53f43fe0 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -20,5 +20,6 @@ $ticketControllers->addController(new CreateTagController); $ticketControllers->addController(new EditTagController); $ticketControllers->addController(new DeleteTagController); $ticketControllers->addController(new GetTagsController); +$ticketControllers->addController(new AddTagController); $ticketControllers->finalize(); diff --git a/server/controllers/ticket/add-tag.php b/server/controllers/ticket/add-tag.php new file mode 100644 index 00000000..0f5e1e79 --- /dev/null +++ b/server/controllers/ticket/add-tag.php @@ -0,0 +1,60 @@ + 'staff_1', + 'requestData' => [ + 'ticketNumber' => [ + 'validation' => DataValidator::validTicketNumber(), + 'error' => ERRORS::INVALID_TICKET + ], + 'tagId' => [ + 'validation' => DataValidator::dataStoreId('tag'), + 'error' => ERRORS::INVALID_TAG + ] + ] + ]; + } + + public function handler() { + $tagId = Controller::request('tagId'); + $tag = Tag::getDataStore($tagId); + $ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber')); + + if ($ticket->sharedTagList->includesId($tagId)) throw new RequestException(ERRORS::TAG_EXISTS); + + $ticket->sharedTagList->add($tag); + $ticket->store(); + + Response::respondSuccess(); + } +} diff --git a/server/controllers/ticket/create-tag.php b/server/controllers/ticket/create-tag.php index e632319d..8324d7fc 100644 --- a/server/controllers/ticket/create-tag.php +++ b/server/controllers/ticket/create-tag.php @@ -3,14 +3,14 @@ use Respect\Validation\Validator as DataValidator; DataValidator::with('CustomValidations', true); /** - * @api {post} /ticket/add-tag Add tag + * @api {post} /ticket/create-tag Create tag * @apiVersion 4.3.2 * - * @apiName Add tag + * @apiName Create tag * * @apiGroup Ticket * - * @apiDescription This path add a new tag. + * @apiDescription This path creates a new tag. * * @apiPermission staff1 * diff --git a/server/models/Tag.php b/server/models/Tag.php index 7d1ea08c..6aa66d6d 100644 --- a/server/models/Tag.php +++ b/server/models/Tag.php @@ -10,11 +10,16 @@ class Tag extends DataStore { 'color' ]; } - public function toArray() { - return[ - 'id'=> $this->id, - 'name'=> $this->name, - 'color' => $this->color - ]; + + public function toArray($minimized = false) { + if($minimized){ + return $this->name; + } else { + return [ + 'id'=> $this->id, + 'name'=> $this->name, + 'color' => $this->color + ]; + } } } diff --git a/server/models/Ticket.php b/server/models/Ticket.php index bf927c81..1530a888 100755 --- a/server/models/Ticket.php +++ b/server/models/Ticket.php @@ -49,7 +49,8 @@ class Ticket extends DataStore { 'unreadStaff', 'language', 'authorEmail', - 'authorName' + 'authorName', + 'sharedTagList' ); } @@ -128,7 +129,8 @@ class Ticket extends DataStore { 'priority' => $this->priority, 'author' => $this->authorToArray(), 'owner' => $this->ownerToArray(), - 'events' => $minimized ? [] : $this->eventsToArray() + 'events' => $minimized ? [] : $this->eventsToArray(), + 'tags' => $this->sharedTagList->toArray(true) ]; } From fad0e8eafbe261c61609c93b8c70fb27d6b55e4b Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Fri, 22 Feb 2019 14:08:49 -0300 Subject: [PATCH 09/13] remove tag path --- server/controllers/system/get-settings.php | 6 ++- server/controllers/ticket.php | 1 + server/controllers/ticket/remove-tag.php | 60 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 server/controllers/ticket/remove-tag.php diff --git a/server/controllers/system/get-settings.php b/server/controllers/system/get-settings.php index f4910d4d..0940df2e 100755 --- a/server/controllers/system/get-settings.php +++ b/server/controllers/system/get-settings.php @@ -56,7 +56,8 @@ class GetSettingsController extends Controller { 'supportedLanguages' => Language::getSupportedLanguages(), 'allowedLanguages' => Language::getAllowedLanguages(), 'session-prefix' => Setting::getSetting('session-prefix')->getValue(), - 'mail-template-header-image' => Setting::getSetting('mail-template-header-image')->getValue() + 'mail-template-header-image' => Setting::getSetting('mail-template-header-image')->getValue(), + 'tags' => Tag::getAll()->toArray() ]; } else { $settingsList = [ @@ -73,7 +74,8 @@ class GetSettingsController extends Controller { 'supportedLanguages' => Language::getSupportedLanguages(), 'allowedLanguages' => Language::getAllowedLanguages(), 'user-system-enabled' => intval(Setting::getSetting('user-system-enabled')->getValue()), - 'session-prefix' => Setting::getSetting('session-prefix')->getValue() + 'session-prefix' => Setting::getSetting('session-prefix')->getValue(), + 'tags' => Tag::getAll()->toArray() ]; } } diff --git a/server/controllers/ticket.php b/server/controllers/ticket.php index 53f43fe0..948f3221 100755 --- a/server/controllers/ticket.php +++ b/server/controllers/ticket.php @@ -21,5 +21,6 @@ $ticketControllers->addController(new EditTagController); $ticketControllers->addController(new DeleteTagController); $ticketControllers->addController(new GetTagsController); $ticketControllers->addController(new AddTagController); +$ticketControllers->addController(new RemoveTagController); $ticketControllers->finalize(); diff --git a/server/controllers/ticket/remove-tag.php b/server/controllers/ticket/remove-tag.php new file mode 100644 index 00000000..73837aaf --- /dev/null +++ b/server/controllers/ticket/remove-tag.php @@ -0,0 +1,60 @@ + 'staff_1', + 'requestData' => [ + 'ticketNumber' => [ + 'validation' => DataValidator::validTicketNumber(), + 'error' => ERRORS::INVALID_TICKET + ], + 'tagId' => [ + 'validation' => DataValidator::dataStoreId('tag'), + 'error' => ERRORS::INVALID_TAG + ] + ] + ]; + } + + public function handler() { + $tagId = Controller::request('tagId'); + $tag = Tag::getDataStore($tagId); + $ticket = Ticket::getByTicketNumber(Controller::request('ticketNumber')); + + if (!$ticket->sharedTagList->includesId($tagId)) throw new RequestException(ERRORS::INVALID_TAG); + + $ticket->sharedTagList->remove($tag); + $ticket->store(); + + Response::respondSuccess(); + } +} From 5bf8ff94bcfeb50996a80d0bb57b8dfb233c82cd Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Fri, 22 Feb 2019 18:26:06 -0300 Subject: [PATCH 10/13] display tags in frontend --- client/src/app-components/ticket-list.js | 22 +++++++--- client/src/app-components/ticket-viewer.js | 39 +++++++++++++++--- client/src/core-components/tag-selector.js | 28 +++++-------- client/src/core-components/tag-selector.scss | 29 +------------- client/src/core-components/tag.js | 42 ++++++++++++++++++++ client/src/core-components/tag.scss | 24 +++++++++++ client/src/lib-app/session-store.js | 3 ++ 7 files changed, 131 insertions(+), 56 deletions(-) create mode 100644 client/src/core-components/tag.js create mode 100644 client/src/core-components/tag.scss diff --git a/client/src/app-components/ticket-list.js b/client/src/app-components/ticket-list.js index 10caf79b..1ce96238 100644 --- a/client/src/app-components/ticket-list.js +++ b/client/src/app-components/ticket-list.js @@ -1,5 +1,6 @@ import React from 'react'; import _ from 'lodash'; +import {connect} from "react-redux"; import i18n from 'lib-app/i18n'; import DateTransformer from 'lib-core/date-transformer'; @@ -11,6 +12,7 @@ import Button from 'core-components/button'; import Tooltip from 'core-components/tooltip'; import Icon from 'core-components/icon'; import Checkbox from 'core-components/checkbox'; +import Tag from 'core-components/tag'; class TicketList extends React.Component { static propTypes = { @@ -182,9 +184,16 @@ class TicketList extends React.Component { ), title: ( - +
+ + {ticket.tags.map((tagName,index) => { + let tag = _.find(this.props.tags, {name:tagName}); + return + })} +
+ ), priority: this.getTicketPriority(ticket.priority), department: ticket.department.name, @@ -257,5 +266,8 @@ class TicketList extends React.Component { } } - -export default TicketList; +export default connect((store) => { + return { + tags: store.config['tags'] + }; +})(TicketList); \ No newline at end of file diff --git a/client/src/app-components/ticket-viewer.js b/client/src/app-components/ticket-viewer.js index 90933a12..87012c71 100644 --- a/client/src/app-components/ticket-viewer.js +++ b/client/src/app-components/ticket-viewer.js @@ -22,6 +22,8 @@ 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 = { @@ -36,10 +38,12 @@ class TicketViewer extends React.Component { userId: React.PropTypes.number, userStaff: React.PropTypes.bool, userDepartments: React.PropTypes.array, - userLevel: React.PropTypes.number + userLevel: React.PropTypes.number, + tags: React.PropTypes.array }; static defaultProps = { + tags: [], editable: false, ticket: { author: {}, @@ -63,6 +67,7 @@ class TicketViewer extends React.Component { render() { const ticket = this.props.ticket; + console.log('tickett',ticket) return (
@@ -105,7 +110,7 @@ class TicketViewer extends React.Component {
{i18n('DEPARTMENT')}
{i18n('AUTHOR')}
-
{i18n('DATE')}
+
{i18n('TAGS')}
@@ -115,7 +120,7 @@ class TicketViewer extends React.Component { onChange={this.onDepartmentDropdownChanged.bind(this)} />
{ticket.author.name}
-
{DateTransformer.transformToString(ticket.date)}
+
{i18n('PRIORITY')}
@@ -153,12 +158,15 @@ class TicketViewer extends React.Component {
{i18n('DEPARTMENT')}
{i18n('AUTHOR')}
-
{i18n('DATE')}
+
{i18n('TAGS')}
{ticket.department.name}
{ticket.author.name}
-
{DateTransformer.transformToString(ticket.date, false)}
+
{ticket.tags.length ? ticket.tags.map((tagName,index) => { + let tag = _.find(this.props.tags, {name:tagName}); + return + }) : i18n('NONE')}
{i18n('PRIORITY')}
@@ -412,7 +420,25 @@ class TicketViewer extends React.Component { } }).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}) { let replaceContentWithCustomResponse = () => { this.setState({ @@ -515,6 +541,7 @@ export default connect((store) => { staffMembersLoaded: store.adminData.staffMembersLoaded, allowAttachments: store.config['allow-attachments'], userSystemEnabled: store.config['user-system-enabled'], - userLevel: store.session.userLevel + userLevel: store.session.userLevel, + tags: store.config['tags'] }; })(TicketViewer); diff --git a/client/src/core-components/tag-selector.js b/client/src/core-components/tag-selector.js index 6af1469d..3de95502 100644 --- a/client/src/core-components/tag-selector.js +++ b/client/src/core-components/tag-selector.js @@ -2,6 +2,7 @@ import React from 'react'; import _ from 'lodash'; import Icon from 'core-components/icon'; import DropDown from 'core-components/drop-down'; +import Tag from 'core-components/tag'; class TagSelector extends React.Component { @@ -32,14 +33,8 @@ class TagSelector extends React.Component { renderSelectedTag(item,index) { - return ( -
event.stopPropagation()} key={index}> - {item.name} - - - -
- ); + return ; + } renderTagOptions() { @@ -50,24 +45,23 @@ class TagSelector extends React.Component { renderTagOption(item,index) { return ( -
+
{item.name}
); } - onRemoveClick(tag) { - + onRemoveClick(tagId) { if(this.props.onRemoveClick){ - this.props.onRemoveClick(tag); - } - } - onTagSelected(tag) { - if(this.props.onTagSelected){ - this.props.onTagSelected(tag); + this.props.onRemoveClick(tagId); } } + onTagSelected(tagId) { + if(this.props.onTagSelected){ + this.props.onTagSelected(tagId); + } + } } export default TagSelector; diff --git a/client/src/core-components/tag-selector.scss b/client/src/core-components/tag-selector.scss index 2cb298af..d03e4712 100644 --- a/client/src/core-components/tag-selector.scss +++ b/client/src/core-components/tag-selector.scss @@ -9,6 +9,7 @@ cursor: text; background-color: white; border: 1px solid $grey; + min-height: 38px; &:focus { 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 { font-size: 13px; color: $dark-grey; diff --git a/client/src/core-components/tag.js b/client/src/core-components/tag.js new file mode 100644 index 00000000..9974cab1 --- /dev/null +++ b/client/src/core-components/tag.js @@ -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 ( +
event.stopPropagation()} > + {this.props.name} + {this.props.showDeleteButton ? this.renderRemoveButton() : null} +
+ ); + } + + renderRemoveButton() { + return ( + + + + ); + } + + getClass() { + let classes = { + 'tag': true, + 'tag_small': this.props.size === 'small', + 'tag_medium': this.props.size === 'medium' + }; + + return classNames(classes); + } +} +export default Tag; diff --git a/client/src/core-components/tag.scss b/client/src/core-components/tag.scss new file mode 100644 index 00000000..81c2fcc3 --- /dev/null +++ b/client/src/core-components/tag.scss @@ -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; + } +} \ No newline at end of file diff --git a/client/src/lib-app/session-store.js b/client/src/lib-app/session-store.js index 540c994b..33a31246 100644 --- a/client/src/lib-app/session-store.js +++ b/client/src/lib-app/session-store.js @@ -62,6 +62,8 @@ class SessionStore { this.setItem('allow-attachments', configs['allow-attachments']); this.setItem('maintenance-mode', configs['maintenance-mode']); this.setItem('max-size', configs['max-size']); + this.setItem('tags', configs['tags']); + } getConfigs() { @@ -78,6 +80,7 @@ class SessionStore { 'allow-attachments': (this.getItem('allow-attachments') * 1), 'maintenance-mode': (this.getItem('maintenance-mode') * 1), 'max-size': this.getItem('max-size'), + 'tags': this.getItem('tags') }; } From 36d09ec919e039238bc5f82d10a5217b4070d3a3 Mon Sep 17 00:00:00 2001 From: Guillermo Date: Mon, 25 Feb 2019 13:34:22 -0300 Subject: [PATCH 11/13] tag tests ruby --- server/controllers/ticket/add-tag.php | 1 + tests/init.rb | 2 + tests/scripts.rb | 8 ++++ tests/ticket/add-tag.rb | 56 +++++++++++++++++++++++++++ tests/ticket/remove-tag.rb | 56 +++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 tests/ticket/add-tag.rb create mode 100644 tests/ticket/remove-tag.rb diff --git a/server/controllers/ticket/add-tag.php b/server/controllers/ticket/add-tag.php index 0f5e1e79..425e333b 100644 --- a/server/controllers/ticket/add-tag.php +++ b/server/controllers/ticket/add-tag.php @@ -20,6 +20,7 @@ DataValidator::with('CustomValidations', true); * @apiUse NO_PERMISSION * @apiUse INVALID_TICKET * @apiUse INVALID_TAG + * @apiUse TAG_EXISTS * * @apiSuccess {Object} data Empty object * diff --git a/tests/init.rb b/tests/init.rb index 7e0eaef4..ca0a90b3 100644 --- a/tests/init.rb +++ b/tests/init.rb @@ -66,5 +66,7 @@ require './ticket/create-tag.rb' require './ticket/edit-tag.rb' require './ticket/get-tags.rb' require './ticket/delete-tag.rb' +require './ticket/add-tag.rb' +require './ticket/delete-tag.rb' require './system/disable-user-system.rb' require './system/get-stats.rb' diff --git a/tests/scripts.rb b/tests/scripts.rb index 304349d0..309d6bba 100644 --- a/tests/scripts.rb +++ b/tests/scripts.rb @@ -107,4 +107,12 @@ class Scripts description: description }) end + def self.createTag(name, color) + request('/ticket/create-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + name: name, + color: color + }) + end end diff --git a/tests/ticket/add-tag.rb b/tests/ticket/add-tag.rb new file mode 100644 index 00000000..14b765fe --- /dev/null +++ b/tests/ticket/add-tag.rb @@ -0,0 +1,56 @@ +describe '/ticket/add-tag' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + Scripts.createTag('test tag', 'orange') + result = Scripts.createTicket('test ticket') + + @ticketNumber = result['ticketNumber'] + + it 'should fail if the tagId is invalid' do + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 100, + ticketNumber: @ticketNumber + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TAG') + + end + + it 'should fail if the ticketNumber is invalid' do + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 3, + ticketNumber: 0 + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TICKET') + end + + it 'should add a tag' do + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 3, + ticketNumber: @ticketNumber + }) + tag_ticket = $database.getRow('tag_ticket', 3 , 'id') + ticket = $database.getRow('ticket', @ticketNumber ,'ticket_number') + + (result['status']).should.equal('success') + end + + it 'should fail if the tag is already attached' do + result = request('/ticket/add-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 3, + ticketNumber: @ticketNumber + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('TAG_EXISTS') + end +end diff --git a/tests/ticket/remove-tag.rb b/tests/ticket/remove-tag.rb new file mode 100644 index 00000000..7fb11043 --- /dev/null +++ b/tests/ticket/remove-tag.rb @@ -0,0 +1,56 @@ +describe '/ticket/remove-tag' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + result = $database.getRow('ticket', 'test ticket' , 'title') + tag = $database.getRow('tag', 'test tag', 'name') + + @ticketNumber = result['ticketNumber'] + + it 'should fail if the ticket number is invalid'do + result = request('/ticket/remove-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: tag['id'], + ticketNumber: 0 + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TICKET') + end + + it 'should fail is the tag id is not valid'do + result = request('/ticket/remove-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: 100, + ticketNumber: @ticketNumber + }) + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TAG') + end + + it 'should remove an attached tag' do + result = request('/ticket/remove-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: tag['id'], + ticketNumber: @ticketNumber + }) + + (result['status']).should.equal('success') + + end + + + it 'should fail if the tag is not attached' do + result = request('/ticket/remove-tag', { + csrf_userid: $csrf_userid, + csrf_token: $csrf_token, + tagId: tag['id'], + ticketNumber: @ticketNumber + }) + + (result['status']).should.equal('fail') + (result['message']).should.equal('INVALID_TAG') + end +end From d96e8b44b1a957007f18f04be67a9b8ef2a1efb1 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Wed, 27 Feb 2019 17:38:22 -0300 Subject: [PATCH 12/13] create custom tag admin editor --- .../tickets/admin-panel-custom-tags-modal.js | 103 ++++++++ .../panel/tickets/admin-panel-custom-tags.js | 239 +++--------------- client/src/app/demo/components-demo-page.js | 2 +- client/src/lib-app/session-store.js | 4 +- 4 files changed, 139 insertions(+), 209 deletions(-) create mode 100644 client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js b/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js new file mode 100644 index 00000000..e271094f --- /dev/null +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js @@ -0,0 +1,103 @@ +import React from 'react'; + +import i18n from 'lib-app/i18n'; +import API from 'lib-app/api-call'; + +import Button from 'core-components/button'; +import Header from 'core-components/header'; +import Form from 'core-components/form'; +import FormField from 'core-components/form-field'; +import SubmitButton from 'core-components/submit-button'; +import ColorSelector from 'core-components/color-selector'; + +class AdminPanelCustomTagsModal extends React.Component { + static contextTypes = { + closeModal: React.PropTypes.func + }; + + static propTypes = { + defaultValues: React.PropTypes.object, + onTagCreated: React.PropTypes.func + }; + + state = { + form: this.props.defaultValues || {name: '', color: '#ff6900'}, + loading: false + }; + + render() { + return ( +
+
+
this.setState({errors})} + loading={this.state.loading}> + + + + + {i18n('SAVE')} + + + +
+ ); + } + + onFormChange(form) { + this.setState({ + form + }); + } + + onSubmitTag(form) { + this.setState({ + loading: true + }); + + API.call({ + path: '/ticket/create-tag', + data: { + name: form.name, + color: form.color, + } + }).then(() => { + this.context.closeModal(); + this.setState({ + loading: false, + errors: {} + }); + + if(this.props.onTagCreated) { + this.props.onTagCreated(); + } + + }).catch((result) => { + + this.setState({ + loading: false, + errors: { + 'name': result.message + } + }); + + }); + } + + onDiscardClick(event) { + event.preventDefault(); + this.context.closeModal(); + this.setState({ + loading: false, + errors: {} + }); + } +} + +export default AdminPanelCustomTagsModal; diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js index 6cc2ec9b..1c5eef4b 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js @@ -1,76 +1,40 @@ import React from 'react'; -import _ from 'lodash'; import {connect} from 'react-redux'; +import AdminPanelCustomTagsModal from 'app/admin/panel/tickets/admin-panel-custom-tags-modal'; + import i18n from 'lib-app/i18n'; import API from 'lib-app/api-call'; -import AdminDataActions from 'actions/admin-data-actions'; +import ConfigActions from 'actions/config-actions'; import AreYouSure from 'app-components/are-you-sure'; -import LanguageSelector from 'app-components/language-selector'; +import ModalContainer from 'app-components/modal-container'; import Icon from 'core-components/icon'; import Button from 'core-components/button'; import Header from 'core-components/header'; -import Listing from 'core-components/listing'; -import Loading from 'core-components/loading'; -import Form from 'core-components/form'; -import FormField from 'core-components/form-field'; -import SubmitButton from 'core-components/submit-button'; -import TextEditor from 'core-components/text-editor'; -import ModalContainer from 'app-components/modal-container'; -import ColorSelector from 'core-components/color-selector'; -class TagList extends React.Component { - constructor(props) { - super(props); +import Tag from 'core-components/tag'; - const list = this.props.tags.map((tag) => { -

{tag.name}

; - }) - } - render() { - return ( -
- {list} -
- ); - } -} class AdminPanelCustomTags extends React.Component { - static defaultProps = { - items: [], - }; - - state = { - formClicked: false, - showForm: false, - formLoading: false, - selectedIndex: -1, - errors: {}, - originalForm: { - title: '', - content: TextEditor.createEmpty(), - language: this.props.language - }, - form: { - title: '', - content: TextEditor.createEmpty(), - language: this.props.language - }, - tagList: {} // + static propTypes = { + tags: React.PropTypes.arrayOf( + React.PropTypes.shape({ + name: React.PropTypes.string, + color: React.PropTypes.string, + id: React.PropTypes.number + }) + ), }; componentDidMount() { - if (!this.props.loaded) { - this.retrieveCustomResponses(); - } + this.retrieveCustomTags(); } render() { return (
- {(this.props.loaded) ? this.renderContent() : this.renderLoading()} + {this.renderContent()}
); } @@ -79,183 +43,46 @@ class AdminPanelCustomTags extends React.Component { return (
- - +
+ {this.props.tags.map((tag) => { + return( + + ) + })}
); } - onCreateTag() { + + openTagModal() { ModalContainer.openModal( -
-
-
- - - - - - {i18n('SAVE')} - - - -
+ ); } - onSubmitTag() { - API.call({ - path: '/ticket/add-tag', - data: { - name: form.title, - color: form.content, - } - }).then(() => { - this.context.closeModal(); - this.updateTagList(); - if(this.props.onChange) { - this.props.onChange(); - } - }).catch(() => { - this.setState({ - loading: false - }); - }); - } - onFormChange(form) { - this.setState({ - values: form - }); - } - onDiscardClick(event) { + onDeleteClick(tagId, event) { event.preventDefault(); - this.context.closeModal(); - } - renderLoading() { - return ( -
- -
- ); + AreYouSure.openModal(i18n('WILL_DELETE_CUSTOM_RESPONSE'), this.deleteCustomTag.bind(this, tagId)); } - renderOptionalButtons() { - return ( -
-
- {this.isEdited() ? : null} -
-
- -
-
- ); - } - - onItemChange(index) { - if(this.isEdited()) { - AreYouSure.openModal(i18n('WILL_LOSE_CHANGES'), this.updateForm.bind(this, index)); - } else { - this.updateForm(index); - } - } - - onSubmit() { - this.setState({ - loading: true - }); - + deleteCustomTag(tagId) { API.call({ - path: (this.props.addForm) ? '/article/add-topic' : '/article/edit-topic', + path: '/ticket/delete-tag', data: { - topicId: this.props.topicId, - name: this.state.values['title'], - icon: this.state.values['icon'], - iconColor: this.state.values['color'], - private: this.state.values['private']*1 + tagId, } }).then(() => { - this.context.closeModal(); - - if(this.props.onChange) { - this.props.onChange(); - } - }).catch(() => { - this.setState({ - loading: false - }); + this.retrieveCustomTags() }); } - onDiscardChangesClick(event) { - event.preventDefault(); - this.onItemChange(this.state.selectedIndex); - } - - onDeleteClick(event) { - event.preventDefault(); - AreYouSure.openModal(i18n('WILL_DELETE_CUSTOM_RESPONSE'), this.deleteCustomResponse.bind(this)); - } - - deleteCustomResponse() { - API.call({ - path: '/ticket/delete-custom-response', - data: { - id: this.props.items[this.state.selectedIndex].id - } - }).then(() => { - this.retrieveCustomResponses(); - this.onItemChange(-1); - }); - } - updateTagList() { - API.call({ - path: '/ticket/get-tags' - }).then(() => { - this.setState({ - tagList: data - }); - }); - } - - updateForm(index) { - let form = _.clone(this.state.form); - - form.title = (this.props.items[index] && this.props.items[index].name) || ''; - form.content = TextEditor.getEditorStateFromHTML((this.props.items[index] && this.props.items[index].content) || ''); - form.language = (this.props.items[index] && this.props.items[index].language) || this.props.language; - - this.setState({ - formClicked: false, - showForm: true, - selectedIndex: index, - formLoading: false, - originalForm: form, - form: form, - errors: {} - }); - } - - retrieveCustomResponses() { - this.props.dispatch(AdminDataActions.retrieveCustomResponses()); - } - - isEdited() { - return this.state.form.title && this.state.formClicked && ( - this.state.form.title != this.state.originalForm.title || - this.state.form.content != this.state.originalForm.content || - this.state.form.language != this.state.originalForm.language - ); + retrieveCustomTags() { + this.props.dispatch(ConfigActions.updateData()); } } export default connect((store) => { return { - allowedLanguages: store.config.allowedLanguages, - language: store.config.language, - loaded: store.adminData.customResponsesLoaded, - items: store.adminData.customResponses + tags: store.config['tags'] }; })(AdminPanelCustomTags); diff --git a/client/src/app/demo/components-demo-page.js b/client/src/app/demo/components-demo-page.js index e421126e..03a97478 100644 --- a/client/src/app/demo/components-demo-page.js +++ b/client/src/app/demo/components-demo-page.js @@ -87,7 +87,7 @@ let DemoPage = React.createClass({ {name: 'tag4', color: 'green'}, {name: 'bug', color: '#eb144c'}, ]} - values={['suggestion','bug']} + values={['suggestion','bug', 'tag4']} onRemoveClick={(e) => console.log('deleted click', e)} onTagSelected={(e) => console.log('selected click', e)} /> diff --git a/client/src/lib-app/session-store.js b/client/src/lib-app/session-store.js index 33a31246..f221df5b 100644 --- a/client/src/lib-app/session-store.js +++ b/client/src/lib-app/session-store.js @@ -62,7 +62,7 @@ class SessionStore { this.setItem('allow-attachments', configs['allow-attachments']); this.setItem('maintenance-mode', configs['maintenance-mode']); this.setItem('max-size', configs['max-size']); - this.setItem('tags', configs['tags']); + this.setItem('tags', JSON.stringify(configs['tags'])); } @@ -80,7 +80,7 @@ class SessionStore { 'allow-attachments': (this.getItem('allow-attachments') * 1), 'maintenance-mode': (this.getItem('maintenance-mode') * 1), 'max-size': this.getItem('max-size'), - 'tags': this.getItem('tags') + 'tags': JSON.parse(this.getItem('tags')) }; } From 5d3a805285cec94b37cc3a7c2be25222ca53df3e Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Fri, 1 Mar 2019 01:15:03 -0300 Subject: [PATCH 13/13] Fix styling of custom tags --- .../src/app/admin/panel/admin-panel-menu.js | 2 +- .../tickets/admin-panel-custom-tags-modal.js | 19 ++++++------- .../admin-panel-custom-tags-modal.scss | 7 +++++ .../panel/tickets/admin-panel-custom-tags.js | 24 +++++++++++------ .../tickets/admin-panel-custom-tags.scss | 27 +++++-------------- client/src/core-components/tag.js | 5 ++-- client/src/core-components/tag.scss | 9 ++++++- client/src/data/fixtures/system-fixtures.js | 6 +++-- client/src/data/fixtures/ticket-fixtures.js | 1 + client/src/data/languages/en.js | 3 +++ tests/system/custom-field-test.rb | 9 +++++++ 11 files changed, 69 insertions(+), 43 deletions(-) create mode 100644 client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.scss create mode 100644 tests/system/custom-field-test.rb diff --git a/client/src/app/admin/panel/admin-panel-menu.js b/client/src/app/admin/panel/admin-panel-menu.js index 3a10570c..aaf8d634 100644 --- a/client/src/app/admin/panel/admin-panel-menu.js +++ b/client/src/app/admin/panel/admin-panel-menu.js @@ -137,7 +137,7 @@ class AdminPanelMenu extends React.Component { level: 2 }, { - name: 'Customtags i18n', + name: i18n('CUSTOM_TAGS'), path: '/admin/panel/tickets/custom-tags', level: 1 } diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js b/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js index e271094f..c3398729 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.js @@ -27,8 +27,8 @@ class AdminPanelCustomTagsModal extends React.Component { render() { return ( -
-
+
+
- - - {i18n('SAVE')} - - +
+ + {i18n('SAVE')} + + +
); diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.scss b/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.scss new file mode 100644 index 00000000..1d1d8b89 --- /dev/null +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags-modal.scss @@ -0,0 +1,7 @@ +.admin-panel-custom-tags-modal { + + &__actions{ + display: flex; + justify-content: space-between; + } +} diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js index 1c5eef4b..8f6f2d90 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.js @@ -41,19 +41,27 @@ class AdminPanelCustomTags extends React.Component { renderContent() { return ( -
-
- +
+
+ +
+
+ {this.props.tags.map(this.renderTag.bind(this))}
- {this.props.tags.map((tag) => { - return( - - ) - })}
); } + renderTag(tag, index) { + return( +
+ +
+ ) + } + openTagModal() { ModalContainer.openModal( diff --git a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss index 7728e33a..d8d8b51c 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss +++ b/client/src/app/admin/panel/tickets/admin-panel-custom-tags.scss @@ -1,31 +1,18 @@ .admin-panel-custom-tags { - &__loading { - height: 300px; - } - &__item-flag { - float: right; - } - - &__actions { + &__content { text-align: left; } - &__save-button { - display: inline-block; - margin-right: 30px; + &__add-button-icon{ + margin-left: 5px; } - &__optional-buttons { - display: inline; + &__tag-list{ + margin-top: 15px; } - &__discard-button { - display: inline-block; - } - - &__delete-button { - display: inline-block; - float: right; + &__tag-container{ + margin-top:5px ; } } diff --git a/client/src/core-components/tag.js b/client/src/core-components/tag.js index 9974cab1..d9518978 100644 --- a/client/src/core-components/tag.js +++ b/client/src/core-components/tag.js @@ -9,7 +9,7 @@ class Tag extends React.Component { color: React.PropTypes.string, showDeleteButton: React.PropTypes.bool, onRemoveClick: React.PropTypes.func, - size: React.PropTypes.oneOf(['small','medium']) + size: React.PropTypes.oneOf(['small','medium','large']) }; render() { @@ -33,7 +33,8 @@ class Tag extends React.Component { let classes = { 'tag': true, 'tag_small': this.props.size === 'small', - 'tag_medium': this.props.size === 'medium' + 'tag_medium': this.props.size === 'medium', + 'tag_large': this.props.size === 'large', }; return classNames(classes); diff --git a/client/src/core-components/tag.scss b/client/src/core-components/tag.scss index 81c2fcc3..4075d6b6 100644 --- a/client/src/core-components/tag.scss +++ b/client/src/core-components/tag.scss @@ -21,4 +21,11 @@ &_small { font-size: 11px; } -} \ No newline at end of file + + &_large { + width: 220px; + display: flex; + justify-content: space-between; + padding: 3px 8px; + } +} diff --git a/client/src/data/fixtures/system-fixtures.js b/client/src/data/fixtures/system-fixtures.js index d12d98b3..12f02503 100755 --- a/client/src/data/fixtures/system-fixtures.js +++ b/client/src/data/fixtures/system-fixtures.js @@ -30,7 +30,8 @@ module.exports = [ 'allowedLanguages': ['en', 'es', 'de', 'fr', 'pt', 'jp', 'ru', 'cn', 'in', 'tr'], 'supportedLanguages': ['en', 'es', 'de'], 'registration': true, - 'user-system-enabled': true + 'user-system-enabled': true, + 'tags': [{id:1,name:'bug', color:'#eb144c'},{id: 2,name:'suggestion',color:'#ff6900'}] } }; @@ -51,7 +52,8 @@ module.exports = [ 'allowedLanguages': ['en', 'es', 'de', 'fr', 'pt', 'jp', 'ru', 'cn', 'in', 'tr'], 'supportedLanguages': ['en', 'es', 'de'], 'registration': true, - 'user-system-enabled': true + 'user-system-enabled': true, + 'tags': [{id:1,name:'bug', color:'#eb144c'},{id: 2,name:'suggestion',color:'#ff6900'}] } }; } diff --git a/client/src/data/fixtures/ticket-fixtures.js b/client/src/data/fixtures/ticket-fixtures.js index 1c456fea..8acb7d3d 100644 --- a/client/src/data/fixtures/ticket-fixtures.js +++ b/client/src/data/fixtures/ticket-fixtures.js @@ -109,6 +109,7 @@ module.exports = [ unreadStaff: true, closed: false, priority: 'medium', + tags: [], author: { id: '3', name: 'Haskell Curry', diff --git a/client/src/data/languages/en.js b/client/src/data/languages/en.js index 9b2a9bc4..70503c62 100644 --- a/client/src/data/languages/en.js +++ b/client/src/data/languages/en.js @@ -48,6 +48,7 @@ export default { 'ALL_TICKETS': 'All Tickets', 'CUSTOM_RESPONSES': 'Custom Responses', 'CUSTOM_TAGS': 'Custom Tags', + 'CREATE_CUSTOM_TAG': 'Create custom tag', 'LIST_USERS': 'List Users', 'BAN_USERS': 'Ban Users', 'LIST_ARTICLES': 'Article List', @@ -103,6 +104,7 @@ export default { 'COLOR': 'Color', 'ADD_NEW_ARTICLE': 'Add new article', 'ADD_ARTICLE': 'Add article', + 'ADD_CUSTOM_TAG': 'Add custom tag', 'LAST_EDITED_IN': 'Last edited in {date}', 'EDIT': 'Edit', 'NO_RESULTS': 'No results', @@ -207,6 +209,7 @@ export default { 'OPTION': 'Option {index}', 'OPTIONS': 'Options', 'FIELD_DESCRIPTION': 'Field description (Optional)', + 'DESCRIPTION_ADD_CUSTOM_TAG': 'here you can add a new custom tag', 'CUSTOM_FIELDS': 'Custom fields', 'CHART_CREATE_TICKET': 'Tickets created', diff --git a/tests/system/custom-field-test.rb b/tests/system/custom-field-test.rb new file mode 100644 index 00000000..bd32071f --- /dev/null +++ b/tests/system/custom-field-test.rb @@ -0,0 +1,9 @@ +describe 'CustomField' do + request('/user/logout') + Scripts.login($staff[:email], $staff[:password], true) + + describe '/system/add-custom field' do + it 'should add custom field with departments' do + end + end +end