[DEV-279] Make ticket table show more tickets overview (#1164)

* make ticket table show more tickets overview

* allow user to choose ticket quantity

* add styles

* fix New Tickets section dropdown

* add commented changes

* correct some code issues
This commit is contained in:
Joel Elias Méndez 2022-04-19 13:17:48 -03:00 committed by GitHub
parent 86cad910ec
commit b3c8819d83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 143 additions and 38 deletions

View File

@ -12,22 +12,22 @@ export default {
}; };
}, },
retrieveMyTickets(page, closed = 0, departmentId = 0) { retrieveMyTickets({page, closed = 0, departmentId = 0, pageSize = 10}) {
return { return {
type: 'MY_TICKETS', type: 'MY_TICKETS',
payload: API.call({ payload: API.call({
path: '/staff/get-tickets', path: '/staff/get-tickets',
data: {page, closed, departmentId} data: {page, closed, departmentId, pageSize}
}) })
}; };
}, },
retrieveNewTickets(page = 1, departmentId = 0) { retrieveNewTickets({page, departmentId = 0, pageSize = 10}) {
return { return {
type: 'NEW_TICKETS', type: 'NEW_TICKETS',
payload: API.call({ payload: API.call({
path: '/staff/get-new-tickets', path: '/staff/get-new-tickets',
data: {page, departmentId} data: {page, departmentId, pageSize}
}) })
}; };
}, },

View File

@ -9,14 +9,15 @@ export default {
payload: {} payload: {}
} }
}, },
retrieveSearchTickets(ticketQueryListState, filters = {}) { retrieveSearchTickets(ticketQueryListState, filters = {}, pageSize = 10) {
return { return {
type: 'SEARCH_TICKETS', type: 'SEARCH_TICKETS',
payload: API.call({ payload: API.call({
path: '/ticket/search', path: '/ticket/search',
data: { data: {
...filters, ...filters,
page: ticketQueryListState.page page: ticketQueryListState.page,
pageSize
} }
}) })
} }

View File

@ -0,0 +1,41 @@
import React from 'react';
import DropDown from 'core-components/drop-down';
import i18n from 'lib-app/i18n';
class PageSizeDropdown extends React.Component {
static propTypes = {
value: React.PropTypes.number,
onChange: React.PropTypes.func,
pages: React.PropTypes.array
}
state = {
selectedIndex: 1
}
render() {
return (
<DropDown {...this.props} onChange={this.onChange.bind(this)} items={this.getPages()} selectedIndex={this.state.selectedIndex} />
)
}
getPages() {
return this.props.pages.map((page) => {
return {content: `${page} / ${i18n('TICKETS')}`}
});
}
onChange(event) {
this.setState({
selectedIndex: event.index
})
if(this.props.onChange) {
this.props.onChange({
pageSize: this.props.pages[event.index]
});
}
}
}
export default PageSizeDropdown;

View File

@ -16,6 +16,7 @@ import Tag from 'core-components/tag';
import Icon from 'core-components/icon'; import Icon from 'core-components/icon';
import Message from 'core-components/message'; import Message from 'core-components/message';
import history from 'lib-app/history'; import history from 'lib-app/history';
import PageSizeDropdown from './page-size-dropdown';
class TicketList extends React.Component { class TicketList extends React.Component {
static propTypes = { static propTypes = {
@ -31,7 +32,8 @@ class TicketList extends React.Component {
]), ]),
closedTicketsShown: React.PropTypes.bool, closedTicketsShown: React.PropTypes.bool,
onClosedTicketsShownChange: React.PropTypes.func, onClosedTicketsShownChange: React.PropTypes.func,
onDepartmentChange: React.PropTypes.func onDepartmentChange: React.PropTypes.func,
showPageSizeDropdown: React.PropTypes.bool
}; };
static defaultProps = { static defaultProps = {
@ -41,7 +43,8 @@ class TicketList extends React.Component {
departments: [], departments: [],
ticketPath: '/dashboard/ticket/', ticketPath: '/dashboard/ticket/',
type: 'primary', type: 'primary',
closedTicketsShown: false closedTicketsShown: false,
showPageSizeDropdown: true
}; };
state = { state = {
@ -49,11 +52,13 @@ class TicketList extends React.Component {
}; };
render() { render() {
const { type, showDepartmentDropdown, onClosedTicketsShownChange } = this.props; const { type, showDepartmentDropdown, onClosedTicketsShownChange, showPageSizeDropdown } = this.props;
const pages = [5, 10, 20, 50];
return ( return (
<div className="ticket-list"> <div className="ticket-list">
<div className="ticket-list__filters"> <div className="ticket-list__filters">
<div className="ticket-list__main-filters">
{(type === 'primary') ? this.renderMessage() : null} {(type === 'primary') ? this.renderMessage() : null}
{ {
((type === 'secondary') && showDepartmentDropdown) ? ((type === 'secondary') && showDepartmentDropdown) ?
@ -61,13 +66,18 @@ class TicketList extends React.Component {
null null
} }
{onClosedTicketsShownChange ? this.renderFilterCheckbox() : null} {onClosedTicketsShownChange ? this.renderFilterCheckbox() : null}
</div>
{
showPageSizeDropdown ?
<PageSizeDropdown className="ticket-list__page-dropdown" pages={pages} onChange={(event) => this.pageSizeChange(event)} /> :
null
}
</div> </div>
<Table {...this.getTableProps()} /> <Table {...this.getTableProps()} />
</div> </div>
); );
} }
renderFilterCheckbox() { renderFilterCheckbox() {
return ( return (
<Checkbox <Checkbox
@ -113,6 +123,12 @@ class TicketList extends React.Component {
} }
} }
pageSizeChange(event) {
const { onPageSizeChange } = this.props;
onPageSizeChange && onPageSizeChange(event.pageSize);
}
getDepartmentDropdownProps() { getDepartmentDropdownProps() {
const { departments, onDepartmentChange } = this.props; const { departments, onDepartmentChange } = this.props;
@ -138,7 +154,7 @@ class TicketList extends React.Component {
loading, loading,
headers: this.getTableHeaders(), headers: this.getTableHeaders(),
rows: this.getTableRows(), rows: this.getTableRows(),
pageSize: 10, pageSize: this.state.tickets,
page, page,
pages, pages,
onPageChange onPageChange

View File

@ -7,16 +7,30 @@
} }
&__filters { &__filters {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px; margin-bottom: 25px;
text-align: left; text-align: left;
} }
&__main-filters {
display: flex;
align-items: center;
}
&__department-selector { &__department-selector {
display: inline-block; display: inline-block;
margin-right: 25px; margin-right: 25px;
text-align: center; text-align: center;
} }
&__page-dropdown {
display: inline-block;
margin-right: 25px;
text-align: center;
}
&__checkbox { &__checkbox {
display: inline-block; display: inline-block;
} }

View File

@ -8,6 +8,8 @@ import TicketList from 'app-components/ticket-list';
import Message from 'core-components/message'; import Message from 'core-components/message';
import searchFiltersActions from '../actions/search-filters-actions'; import searchFiltersActions from '../actions/search-filters-actions';
import queryString from 'query-string'; import queryString from 'query-string';
import searchTicketsUtils from 'lib-app/search-tickets-utils';
import history from 'lib-app/history';
class TicketQueryList extends React.Component { class TicketQueryList extends React.Component {
@ -65,6 +67,7 @@ class TicketQueryList extends React.Component {
orderBy: filters.orderBy ? JSON.parse(filters.orderBy) : filters.orderBy, orderBy: filters.orderBy ? JSON.parse(filters.orderBy) : filters.orderBy,
showOrderArrows: true, showOrderArrows: true,
onChangeOrderBy: onChangeOrderBy, onChangeOrderBy: onChangeOrderBy,
showPageSizeDropdown: false
}; };
} }

View File

@ -26,10 +26,11 @@ class AdminPanelMyTickets extends React.Component {
state = { state = {
closedTicketsShown: false, closedTicketsShown: false,
departmentId: null, departmentId: null,
pageSize: 10
}; };
componentDidMount() { componentDidMount() {
this.retrieveMyTickets(); this.retrieveMyTickets({});
} }
render() { render() {
@ -68,10 +69,14 @@ class AdminPanelMyTickets extends React.Component {
onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this), onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this),
pages, pages,
page, page,
onPageChange: event => this.retrieveMyTickets(event.target.value), onPageChange: event => this.retrieveMyTickets({page: event.target.value}),
onDepartmentChange: departmentId => { onDepartmentChange: departmentId => {
this.setState({departmentId}); this.setState({departmentId})
this.retrieveMyTickets(1, closedTicketsShown, departmentId); this.retrieveMyTickets({page: 1, departmentId});
},
onPageSizeChange: pageSize => {
this.setState({pageSize});
this.retrieveMyTickets({page: 1, pageSize});
}, },
}; };
} }
@ -81,7 +86,7 @@ class AdminPanelMyTickets extends React.Component {
return { return {
closedTicketsShown: !state.closedTicketsShown closedTicketsShown: !state.closedTicketsShown
}; };
}, () => this.retrieveMyTickets()); }, () => this.retrieveMyTickets({}));
} }
onCreateTicket() { onCreateTicket() {
@ -100,11 +105,11 @@ class AdminPanelMyTickets extends React.Component {
onCreateTicketSuccess() { onCreateTicketSuccess() {
ModalContainer.closeModal(); ModalContainer.closeModal();
this.retrieveMyTickets(); this.retrieveMyTickets({});
} }
retrieveMyTickets(page = this.props.page, closed = this.state.closedTicketsShown, departmentId = this.state.departmentId) { retrieveMyTickets({page = this.props.page, closed = this.state.closedTicketsShown, departmentId = this.state.departmentId, pageSize = this.state.pageSize}) {
this.props.dispatch(AdminDataAction.retrieveMyTickets(page, closed * 1, departmentId)); this.props.dispatch(AdminDataAction.retrieveMyTickets({page, closed: closed * 1, departmentId, pageSize}));
} }
} }

View File

@ -20,10 +20,11 @@ class AdminPanelNewTickets extends React.Component {
state = { state = {
departmentId: null, departmentId: null,
pageSize: 10
}; };
componentDidMount() { componentDidMount() {
this.retrieveNewTickets(); this.retrieveNewTickets({});
} }
render() { render() {
@ -47,16 +48,20 @@ class AdminPanelNewTickets extends React.Component {
ticketPath: '/admin/panel/tickets/view-ticket/', ticketPath: '/admin/panel/tickets/view-ticket/',
page: this.props.page, page: this.props.page,
pages: this.props.pages, pages: this.props.pages,
onPageChange: event => this.retrieveNewTickets(event.target.value), onPageChange: event => this.retrieveNewTickets({page: event.target.value}),
onDepartmentChange: departmentId => { onDepartmentChange: departmentId => {
this.setState({departmentId}); this.setState({departmentId});
this.retrieveNewTickets(1, departmentId); this.retrieveNewTickets({page: 1, departmentId});
}, },
onPageSizeChange: pageSize => {
this.setState({pageSize});
this.retrieveNewTickets({page: 1, pageSize});
}
}; };
} }
retrieveNewTickets(page = this.props.page, departmentId = this.state.departmentId) { retrieveNewTickets({page = this.props.page, departmentId = this.state.departmentId, pageSize = this.state.pageSize }) {
this.props.dispatch(AdminDataAction.retrieveNewTickets(page, departmentId)); this.props.dispatch(AdminDataAction.retrieveNewTickets({page, departmentId, pageSize}));
} }
} }

View File

@ -38,7 +38,8 @@ export function updateSearchTicketsFromURL() {
...store.getState().searchFilters.ticketQueryListState, ...store.getState().searchFilters.ticketQueryListState,
page: (currentSearchParams.page || INITIAL_PAGE)*1 page: (currentSearchParams.page || INITIAL_PAGE)*1
}, },
searchTicketsUtils.prepareFiltersForAPI(listConfig.filters) searchTicketsUtils.prepareFiltersForAPI(listConfig.filters),
currentSearchParams.pageSize
)); ));
}); });
} }

View File

@ -411,6 +411,7 @@ export default {
'INVALID_SUPERVISED_USERS': 'Invalid supervised users', 'INVALID_SUPERVISED_USERS': 'Invalid supervised users',
'SUPERVISOR_CAN_NOT_SUPERVISE_HIMSELF': 'Supervisor can not supervise himself', 'SUPERVISOR_CAN_NOT_SUPERVISE_HIMSELF': 'Supervisor can not supervise himself',
'NAME_ALREADY_USED': 'Name already used', 'NAME_ALREADY_USED': 'Name already used',
'PAGESIZE_ERROR': 'Invalid page size',
//MESSAGES //MESSAGES
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.', 'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',

View File

@ -38,6 +38,10 @@ class GetNewTicketsStaffController extends Controller {
'page' => [ 'page' => [
'validation' => DataValidator::numeric(), 'validation' => DataValidator::numeric(),
'error' => ERRORS::INVALID_PAGE 'error' => ERRORS::INVALID_PAGE
],
'pageSize' => [
'validation' => DataValidator::intVal()->between(5, 50),
'error' => ERRORS::PAGESIZE_ERROR
] ]
] ]
]; ];
@ -45,6 +49,7 @@ class GetNewTicketsStaffController extends Controller {
public function handler() { public function handler() {
$page = Controller::request('page'); $page = Controller::request('page');
$departmentId = Controller::request('departmentId'); $departmentId = Controller::request('departmentId');
$pageSize = Controller::request('pageSize');
if (Ticket::isTableEmpty()) { if (Ticket::isTableEmpty()) {
Response::respondSuccess([ Response::respondSuccess([
@ -77,14 +82,14 @@ class GetNewTicketsStaffController extends Controller {
$countTotal = Ticket::count($query); $countTotal = Ticket::count($query);
$query .= ' ORDER BY unread_staff DESC'; $query .= ' ORDER BY unread_staff DESC';
$query .= ' LIMIT 10 OFFSET ' . ($page-1)*10; $query .= ' LIMIT ' . $pageSize . ' OFFSET ' . ($page-1)*10;
$ticketList = Ticket::find($query); $ticketList = Ticket::find($query);
Response::respondSuccess([ Response::respondSuccess([
'tickets' => $ticketList->toArray(true), 'tickets' => $ticketList->toArray(true),
'page' => $page, 'page' => $page,
'pages' => ceil($countTotal / 10) 'pages' => ceil($countTotal / $pageSize)
]); ]);
} }
} }

View File

@ -38,6 +38,10 @@ class GetTicketStaffController extends Controller {
'page' => [ 'page' => [
'validation' => DataValidator::numeric(), 'validation' => DataValidator::numeric(),
'error' => ERRORS::INVALID_PAGE 'error' => ERRORS::INVALID_PAGE
],
'pageSize' => [
'validation' => DataValidator::intVal()->between(5, 50),
'error' => ERRORS::PAGESIZE_ERROR
] ]
] ]
]; ];
@ -48,7 +52,8 @@ class GetTicketStaffController extends Controller {
$closed = Controller::request('closed'); $closed = Controller::request('closed');
$page = Controller::request('page'); $page = Controller::request('page');
$departmentId = Controller::request('departmentId'); $departmentId = Controller::request('departmentId');
$offset = ($page-1)*10; $pageSize = Controller::request('pageSize');
$offset = ($page-1)*$pageSize;
$condition = 'TRUE'; $condition = 'TRUE';
$bindings = []; $bindings = [];
@ -65,7 +70,7 @@ class GetTicketStaffController extends Controller {
$countTotal = $user->withCondition($condition, $bindings)->countShared('ticket'); $countTotal = $user->withCondition($condition, $bindings)->countShared('ticket');
$condition .= ' LIMIT 10 OFFSET ?'; $condition .= ' LIMIT ' . $pageSize . ' OFFSET ?';
$bindings[] = $offset; $bindings[] = $offset;
$tickets = $user->withCondition($condition, $bindings)->sharedTicketList->toArray(true); $tickets = $user->withCondition($condition, $bindings)->sharedTicketList->toArray(true);
@ -73,7 +78,7 @@ class GetTicketStaffController extends Controller {
Response::respondSuccess([ Response::respondSuccess([
'tickets' => $tickets, 'tickets' => $tickets,
'page' => $page, 'page' => $page,
'pages' => ceil($countTotal / 10) 'pages' => ceil($countTotal / $pageSize)
]); ]);
} }
} }

View File

@ -99,6 +99,10 @@ class SearchController extends Controller {
'validation' => DataValidator::oneOf(DataValidator::validOrderBy(),DataValidator::nullType()), 'validation' => DataValidator::oneOf(DataValidator::validOrderBy(),DataValidator::nullType()),
'error' => ERRORS::INVALID_ORDER_BY 'error' => ERRORS::INVALID_ORDER_BY
], ],
'pageSize' => [
'validation' => DataValidator::intVal()->between(5, 50),
'error' => ERRORS::PAGESIZE_ERROR
]
] ]
]; ];
} }
@ -124,7 +128,8 @@ class SearchController extends Controller {
'orderBy' => json_decode(Controller::request('orderBy'),true), 'orderBy' => json_decode(Controller::request('orderBy'),true),
'page' => Controller::request('page'), 'page' => Controller::request('page'),
'allowedDepartments' => $allowedDepartmentsId, 'allowedDepartments' => $allowedDepartmentsId,
'staffId' => Controller::getLoggedUser()->id 'staffId' => Controller::getLoggedUser()->id,
'pageSize' => Controller::request('pageSize')
]; ];
$query = $this->getSQLQuery($inputs); $query = $this->getSQLQuery($inputs);
$queryWithOrder = $this->getSQLQueryWithOrder($inputs, $query); $queryWithOrder = $this->getSQLQueryWithOrder($inputs, $query);
@ -137,7 +142,7 @@ class SearchController extends Controller {
} }
Response::respondSuccess([ Response::respondSuccess([
'tickets' => $ticketList, 'tickets' => $ticketList,
'pages' => ceil($totalCount / 10), 'pages' => ceil($totalCount / $inputs['pageSize']),
'page' => $inputs['page'] ? ($inputs['page']*1) : 1 'page' => $inputs['page'] ? ($inputs['page']*1) : 1
]); ]);
} }
@ -154,12 +159,14 @@ class SearchController extends Controller {
} }
public function getSQLQueryWithOrder($inputs, $query) { public function getSQLQueryWithOrder($inputs, $query) {
$pageSize = $inputs['pageSize'];
$order = ""; $order = "";
$query = "SELECT ticket.id " . $query; $query = "SELECT ticket.id " . $query;
$this->setQueryOrder($inputs, $order); $this->setQueryOrder($inputs, $order);
$inputs['page'] ? $page = $inputs['page'] : $page = 1 ; $inputs['page'] ? $page = $inputs['page'] : $page = 1 ;
$query .= $order ." LIMIT 10 OFFSET " . (($page-1)*10); $query .= $order . ' LIMIT ' . $pageSize . ' OFFSET ' . ($page-1)*10;
return $query; return $query;
} }

View File

@ -415,4 +415,5 @@ class ERRORS {
const INVALID_SUPERVISED_USERS = 'INVALID_SUPERVISED_USERS'; const INVALID_SUPERVISED_USERS = 'INVALID_SUPERVISED_USERS';
const INVALID_USER_SEARCH_OPTION = 'INVALID_USER_SEARCH_OPTION'; const INVALID_USER_SEARCH_OPTION = 'INVALID_USER_SEARCH_OPTION';
const TICKET_CONTENT_CANNOT_BE_EDITED = 'TICKET_CONTENT_CANNOT_BE_EDITED'; const TICKET_CONTENT_CANNOT_BE_EDITED = 'TICKET_CONTENT_CANNOT_BE_EDITED';
const PAGESIZE_ERROR = 'PAGESIZE_ERROR';
} }