From e8848d898eb4ba3f01311389dcf41ce352411c0b Mon Sep 17 00:00:00 2001 From: Ivan Diaz Date: Thu, 15 Nov 2018 20:33:08 -0300 Subject: [PATCH] Paginate all ticket list request, avoid returting too much data --- client/src/actions/admin-data-actions.js | 8 ++--- .../panel/tickets/admin-panel-all-tickets.js | 6 ++-- .../panel/tickets/admin-panel-my-tickets.js | 25 +++++++++----- .../panel/tickets/admin-panel-new-tickets.js | 13 +++++-- client/src/reducers/admin-data-reducer.js | 17 ++++++++-- server/controllers/staff/get-all-tickets.php | 11 ++++-- server/controllers/staff/get-new-tickets.php | 34 +++++++++++++++---- server/controllers/staff/get-tickets.php | 31 ++++++++++++++--- server/libs/DataStoreList.php | 18 +++++----- server/models/DataStore.php | 12 ++++--- tests/staff/get-new-tickets.rb | 5 +-- tests/staff/get-tickets.rb | 5 ++- 12 files changed, 135 insertions(+), 50 deletions(-) diff --git a/client/src/actions/admin-data-actions.js b/client/src/actions/admin-data-actions.js index 8926b1f4..b1cccc20 100644 --- a/client/src/actions/admin-data-actions.js +++ b/client/src/actions/admin-data-actions.js @@ -12,22 +12,22 @@ export default { }; }, - retrieveMyTickets(closed = 0) { + retrieveMyTickets(page, closed = 0) { return { type: 'MY_TICKETS', payload: API.call({ path: '/staff/get-tickets', - data: {closed} + data: {page, closed} }) }; }, - retrieveNewTickets() { + retrieveNewTickets(page = 1) { return { type: 'NEW_TICKETS', payload: API.call({ path: '/staff/get-new-tickets', - data: {} + data: {page} }) }; }, diff --git a/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js index bbc7ef70..736bc3ae 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-all-tickets.js @@ -21,7 +21,7 @@ class AdminPanelAllTickets extends React.Component { state = { page: 1, query: '', - closedTicketsShown: false + closedTicketsShown: 0 }; componentDidMount() { @@ -46,7 +46,7 @@ class AdminPanelAllTickets extends React.Component { this.props.dispatch(AdminDataAction.retrieveAllTickets( this.state.page, this.state.query, - this.state.closedTicketsShown*1 + this.state.closedTicketsShown * 1 )); } @@ -63,7 +63,7 @@ class AdminPanelAllTickets extends React.Component { page: this.state.page, pages: this.props.pages, closedTicketsShown: this.state.closedTicketsShown, - onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this) + onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this) }; } diff --git a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js index a5f827ac..8a07c702 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-my-tickets.js @@ -18,15 +18,17 @@ class AdminPanelMyTickets extends React.Component { static defaultProps = { userId: 0, departments: [], - tickets: [] + tickets: [], + page: 1, + pages: 0, }; state = { - closedTicketsShown: false + closedTicketsShown: false, }; componentDidMount() { - this.props.dispatch(AdminDataAction.retrieveMyTickets()); + this.retrieveMyTickets() } render() { @@ -52,7 +54,10 @@ class AdminPanelMyTickets extends React.Component { loading: this.props.loading, ticketPath: '/admin/panel/tickets/view-ticket/', closedTicketsShown: this.state.closedTicketsShown, - onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this) + onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this), + pages: this.props.pages, + page: this.props.page, + onPageChange: event => this.retrieveMyTickets(event.target.value) }; } @@ -61,9 +66,7 @@ class AdminPanelMyTickets extends React.Component { return { closedTicketsShown: !state.closedTicketsShown }; - }, () => { - this.props.dispatch(AdminDataAction.retrieveMyTickets(this.state.closedTicketsShown * 1)); - }); + }, () => this.retrieveMyTickets()); } onCreateTicket() { @@ -79,7 +82,11 @@ class AdminPanelMyTickets extends React.Component { onCreateTicketSuccess() { ModalContainer.closeModal(); - this.props.dispatch(AdminDataAction.retrieveMyTickets()); + this.retrieveMyTickets(); + } + + retrieveMyTickets(page = this.props.page, closed = this.state.closedTicketsShown) { + this.props.dispatch(AdminDataAction.retrieveMyTickets(page, closed * 1)); } } @@ -88,6 +95,8 @@ export default connect((store) => { userId: store.session.userId, departments: store.session.userDepartments, tickets: store.adminData.myTickets, + page: store.adminData.myTicketsPage, + pages: store.adminData.myTicketsPages, loading: !store.adminData.myTicketsLoaded, error: store.adminData.myTicketsError }; diff --git a/client/src/app/admin/panel/tickets/admin-panel-new-tickets.js b/client/src/app/admin/panel/tickets/admin-panel-new-tickets.js index 86b5e5be..6955905e 100644 --- a/client/src/app/admin/panel/tickets/admin-panel-new-tickets.js +++ b/client/src/app/admin/panel/tickets/admin-panel-new-tickets.js @@ -18,7 +18,7 @@ class AdminPanelNewTickets extends React.Component { }; componentDidMount() { - this.props.dispatch(AdminDataAction.retrieveNewTickets()); + this.retrieveNewTickets() } render() { @@ -39,9 +39,16 @@ class AdminPanelNewTickets extends React.Component { tickets: this.props.tickets, type: 'secondary', loading: this.props.loading, - ticketPath: '/admin/panel/tickets/view-ticket/' + ticketPath: '/admin/panel/tickets/view-ticket/', + page: this.props.page, + pages: this.props.pages, + onPageChange: event => this.retrieveNewTickets(event.target.value) }; } + + retrieveNewTickets(page = this.props.page) { + this.props.dispatch(AdminDataAction.retrieveNewTickets(page)); + } } export default connect((store) => { @@ -49,6 +56,8 @@ export default connect((store) => { userId: store.session.userId, departments: store.session.userDepartments, tickets: store.adminData.newTickets, + page: store.adminData.newTicketsPage, + pages: store.adminData.newTicketsPages, loading: !store.adminData.newTicketsLoaded, error: store.adminData.newTicketsError }; diff --git a/client/src/reducers/admin-data-reducer.js b/client/src/reducers/admin-data-reducer.js index 5aa12b2d..abb70499 100644 --- a/client/src/reducers/admin-data-reducer.js +++ b/client/src/reducers/admin-data-reducer.js @@ -11,14 +11,20 @@ class AdminDataReducer extends Reducer { customResponsesLoaded: false, myTickets: [], + myTicketsPage: 1, + myTicketsPages: 1, myTicketsLoaded: false, myTicketsError: false, newTickets: [], + newTicketsPage: 1, + newTicketsPages: 1, newTicketsLoaded: false, newTicketsError: false, allTickets: [], + allTicketsPage: 1, + allTicketsPages: 1, allTicketsLoaded: false, allTicketsError: false, @@ -61,7 +67,9 @@ class AdminDataReducer extends Reducer { onMyTicketsRetrieved(state, payload) { return _.extend({}, state, { - myTickets: payload.data, + myTickets: payload.data.tickets, + myTicketsPage: payload.data.page * 1, + myTicketsPages: payload.data.pages * 1, myTicketsLoaded: true }); } @@ -82,7 +90,9 @@ class AdminDataReducer extends Reducer { onNewTicketsRetrieved(state, payload) { return _.extend({}, state, { - newTickets: payload.data, + newTickets: payload.data.tickets, + newTicketsPage: payload.data.page * 1, + newTicketsPages: payload.data.pages * 1, newTicketsLoaded: true }); } @@ -104,7 +114,8 @@ class AdminDataReducer extends Reducer { onAllTicketsRetrieved(state, payload) { return _.extend({}, state, { allTickets: payload.data.tickets, - allTicketsPages: payload.data.pages, + allTicketsPage: payload.data.page * 1, + allTicketsPages: payload.data.pages * 1, allTicketsLoaded: true }); } diff --git a/server/controllers/staff/get-all-tickets.php b/server/controllers/staff/get-all-tickets.php index 7c38a489..17b6be71 100755 --- a/server/controllers/staff/get-all-tickets.php +++ b/server/controllers/staff/get-all-tickets.php @@ -52,7 +52,7 @@ class GetAllTicketsStaffController extends Controller { } Response::respondSuccess([ - 'tickets' => $this->getTicketList()->toArray(), + 'tickets' => $this->getTicketList()->toArray(true), 'pages' => $this->getTotalPages() ]); } @@ -81,9 +81,14 @@ class GetAllTicketsStaffController extends Controller { } private function getTotalPages() { - $query = $this->getStaffDepartmentsQueryFilter(); + $query = $this->getSearchQuery(); + $query .= $this->getStaffDepartmentsQueryFilter(); + $query .= $this->getClosedFilter(); - return ceil(Ticket::count($query) / 10); + return ceil(Ticket::count($query, [ + Controller::request('query') . '%', + '%' . Controller::request('query') . '%' + ]) / 10); } private function getStaffDepartmentsQueryFilter() { diff --git a/server/controllers/staff/get-new-tickets.php b/server/controllers/staff/get-new-tickets.php index c342ac11..381ca2a3 100755 --- a/server/controllers/staff/get-new-tickets.php +++ b/server/controllers/staff/get-new-tickets.php @@ -14,9 +14,15 @@ use Respect\Validation\Validator as DataValidator; * * @apiPermission staff1 * - * @apiUse NO_PERMISSION + * @apiParam {Number} page The page number. * - * @apiSuccess {[Ticket](#api-Data_Structures-ObjectTicket)[]} data Array of new tickets. + * @apiUse NO_PERMISSION + * @apiUse INVALID_PAGE + * + * @apiSuccess {Object} data Information about a tickets and quantity of pages. + * @apiSuccess {[Ticket](#api-Data_Structures-ObjectTicket)[]} data.tickets Array of new tickets of the current page. + * @apiSuccess {Number} data.page Number of current page. + * @apiSuccess {Number} data.pages Quantity of pages. * */ @@ -27,7 +33,12 @@ class GetNewTicketsStaffController extends Controller { public function validations() { return[ 'permission' => 'staff_1', - 'requestData' => [] + 'requestData' => [ + 'page' => [ + 'validation' => DataValidator::numeric(), + 'error' => ERRORS::INVALID_PAGE + ] + ] ]; } public function handler() { @@ -37,6 +48,8 @@ class GetNewTicketsStaffController extends Controller { } $user = Controller::getLoggedUser(); + $page = Controller::request('page'); + $query = ' ('; foreach ($user->sharedDepartmentList as $department) { $query .= 'department_id=' . $department->id . ' OR '; @@ -45,13 +58,22 @@ class GetNewTicketsStaffController extends Controller { $ownerExists = RedBean::exec('SHOW COLUMNS FROM ticket LIKE \'owner_id\''); if($ownerExists != 0) { - $query .= 'FALSE) AND owner_id IS NULL'; + $query .= 'FALSE) AND closed = 0 AND owner_id IS NULL'; } else { - $query .= 'FALSE)'; + $query .= 'FALSE) AND closed = 0'; } + $countTotal = Ticket::count($query); + + $query .= ' ORDER BY unread_staff DESC'; + $query .= ' LIMIT 10 OFFSET ' . ($page-1)*10; + $ticketList = Ticket::find($query); - Response::respondSuccess($ticketList->toArray()); + Response::respondSuccess([ + 'tickets' => $ticketList->toArray(true), + 'page' => $page, + 'pages' => ceil($countTotal / 10) + ]); } } diff --git a/server/controllers/staff/get-tickets.php b/server/controllers/staff/get-tickets.php index 137cb7eb..e5393ca9 100755 --- a/server/controllers/staff/get-tickets.php +++ b/server/controllers/staff/get-tickets.php @@ -13,11 +13,16 @@ use Respect\Validation\Validator as DataValidator; * * @apiPermission staff1 * + * @apiParam {Number} page The page number. * @apiParam {bool} closed Include closed tickets in the response. * * @apiUse NO_PERMISSION - * - * @apiSuccess {[Ticket](#api-Data_Structures-ObjectTicket)[]} data Array of tickets assigned to the staff + * @apiUse INVALID_PAGE + * + * @apiSuccess {Object} data Information about a tickets and quantity of pages. + * @apiSuccess {[Ticket](#api-Data_Structures-ObjectTicket)[]} data.tickets Array of tickets assigned to the staff of the current page. + * @apiSuccess {Number} data.page Number of current page. + * @apiSuccess {Number} data.pages Quantity of pages. * */ @@ -28,17 +33,33 @@ class GetTicketStaffController extends Controller { public function validations() { return [ 'permission' => 'staff_1', - 'requestData' => [] + 'requestData' => [ + 'page' => [ + 'validation' => DataValidator::numeric(), + 'error' => ERRORS::INVALID_PAGE + ] + ] ]; } public function handler() { $user = Controller::getLoggedUser(); $closed = Controller::request('closed'); + $page = Controller::request('page'); + $offset = ($page-1)*10; + if ($closed) { - Response::respondSuccess($user->sharedTicketList->toArray()); + $tickets = $user->withCondition(' TRUE LIMIT 10 OFFSET ?', [$offset])->sharedTicketList->toArray(true); + $countTotal = $user->countShared('ticket'); } else { - Response::respondSuccess($user->withCondition('closed = ?', ['0'])->sharedTicketList->toArray()); + $tickets = $user->withCondition(' closed = ? LIMIT 10 OFFSET ?', ['0', $offset])->sharedTicketList->toArray(true); + $countTotal = $user->withCondition(' closed = ?', ['0'])->countShared('ticket'); } + + Response::respondSuccess([ + 'tickets' => $tickets, + 'page' => $page, + 'pages' => ceil($countTotal / 10) + ]); } } diff --git a/server/libs/DataStoreList.php b/server/libs/DataStoreList.php index da995595..6c666931 100755 --- a/server/libs/DataStoreList.php +++ b/server/libs/DataStoreList.php @@ -3,14 +3,14 @@ require_once 'models/DataStore.php'; class DataStoreList implements IteratorAggregate { private $list = []; - + public static function getList($type, $beanList) { $dataStoreList = new DataStoreList(); - + foreach ($beanList as $bean) { $dataStoreList->add(new $type($bean)); } - + return $dataStoreList; } @@ -51,21 +51,21 @@ class DataStoreList implements IteratorAggregate { public function toBeanList() { $beanList = []; - + foreach($this->list as $item) { $item->updateBeanProperties(); $beanList[] = $item->getBeanInstance(); } - + return $beanList; } - - public function toArray() { + + public function toArray($minimized = false) { $array = []; foreach($this->list as $item) { $item->updateBeanProperties(); - $array[] = $item->toArray(); + $array[] = $item->toArray($minimized); } return $array; @@ -80,4 +80,4 @@ class DataStoreList implements IteratorAggregate { return -1; } -} \ No newline at end of file +} diff --git a/server/models/DataStore.php b/server/models/DataStore.php index d4310fdc..b13f7294 100755 --- a/server/models/DataStore.php +++ b/server/models/DataStore.php @@ -33,13 +33,13 @@ abstract class DataStore { } public static function find($query = '', $matches = []) { $beanList = RedBean::find(static::TABLE, $query, $matches); - + return DataStoreList::getList(ucfirst(static::TABLE), $beanList); } - + public static function findOne($query = '', $matches = []) { $bean = RedBean::findOne(static::TABLE, $query, $matches); - + return ($bean) ? new static($bean) : new NullDataStore(); } @@ -151,7 +151,11 @@ abstract class DataStore { } public function withCondition($condition, $values) { - return new static($this->_bean->withCondition($condition, $values)); + return new static($this->_bean->withCondition($condition, $values)); + } + + public function countShared($shared) { + return $this->_bean->countShared($shared); } private function updateBeanProp($key, $value) { diff --git a/tests/staff/get-new-tickets.rb b/tests/staff/get-new-tickets.rb index 7561aa1b..bb3a94da 100644 --- a/tests/staff/get-new-tickets.rb +++ b/tests/staff/get-new-tickets.rb @@ -2,13 +2,14 @@ describe '/staff/get-new-tickets' do request('/user/logout') Scripts.login($staff[:email], $staff[:password], true) - it 'should get news tickets' do + it 'should get new tickets' do result = request('/staff/get-new-tickets', { + page: 1, csrf_userid: $csrf_userid, csrf_token: $csrf_token }) (result['status']).should.equal('success') - (result['data'].size).should.equal(9) + (result['data']['tickets'].size).should.equal(8) end end diff --git a/tests/staff/get-tickets.rb b/tests/staff/get-tickets.rb index af0bce51..7e7e9642 100644 --- a/tests/staff/get-tickets.rb +++ b/tests/staff/get-tickets.rb @@ -7,21 +7,24 @@ describe '/staff/get-tickets' do ticket = $database.getRow('ticket', 1 , 'id') request('/staff/assign-ticket', { ticketNumber: ticket['ticket_number'], + page: 1, csrf_userid: $csrf_userid, csrf_token: $csrf_token }) ticket = $database.getRow('ticket', 2 , 'id') request('/staff/assign-ticket', { ticketNumber: ticket['ticket_number'], + page: 1, csrf_userid: $csrf_userid, csrf_token: $csrf_token }) result = request('/staff/get-tickets', { + page: 1, csrf_userid: $csrf_userid, csrf_token: $csrf_token }) (result['status']).should.equal('success') - (result['data'].size).should.equal(5) + (result['data']['tickets'].size).should.equal(5) end end