[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 {
type: 'MY_TICKETS',
payload: API.call({
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 {
type: 'NEW_TICKETS',
payload: API.call({
path: '/staff/get-new-tickets',
data: {page, departmentId}
data: {page, departmentId, pageSize}
})
};
},

View File

@ -9,14 +9,15 @@ export default {
payload: {}
}
},
retrieveSearchTickets(ticketQueryListState, filters = {}) {
retrieveSearchTickets(ticketQueryListState, filters = {}, pageSize = 10) {
return {
type: 'SEARCH_TICKETS',
payload: API.call({
path: '/ticket/search',
data: {
...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 Message from 'core-components/message';
import history from 'lib-app/history';
import PageSizeDropdown from './page-size-dropdown';
class TicketList extends React.Component {
static propTypes = {
@ -31,7 +32,8 @@ class TicketList extends React.Component {
]),
closedTicketsShown: React.PropTypes.bool,
onClosedTicketsShownChange: React.PropTypes.func,
onDepartmentChange: React.PropTypes.func
onDepartmentChange: React.PropTypes.func,
showPageSizeDropdown: React.PropTypes.bool
};
static defaultProps = {
@ -41,7 +43,8 @@ class TicketList extends React.Component {
departments: [],
ticketPath: '/dashboard/ticket/',
type: 'primary',
closedTicketsShown: false
closedTicketsShown: false,
showPageSizeDropdown: true
};
state = {
@ -49,25 +52,32 @@ class TicketList extends React.Component {
};
render() {
const { type, showDepartmentDropdown, onClosedTicketsShownChange } = this.props;
const { type, showDepartmentDropdown, onClosedTicketsShownChange, showPageSizeDropdown } = this.props;
const pages = [5, 10, 20, 50];
return (
<div className="ticket-list">
<div className="ticket-list__filters">
{(type === 'primary') ? this.renderMessage() : null}
<div className="ticket-list__main-filters">
{(type === 'primary') ? this.renderMessage() : null}
{
((type === 'secondary') && showDepartmentDropdown) ?
this.renderDepartmentsDropDown() :
null
}
{onClosedTicketsShownChange ? this.renderFilterCheckbox() : null}
</div>
{
((type === 'secondary') && showDepartmentDropdown) ?
this.renderDepartmentsDropDown() :
showPageSizeDropdown ?
<PageSizeDropdown className="ticket-list__page-dropdown" pages={pages} onChange={(event) => this.pageSizeChange(event)} /> :
null
}
{onClosedTicketsShownChange ? this.renderFilterCheckbox() : null}
</div>
<Table {...this.getTableProps()} />
</div>
);
}
renderFilterCheckbox() {
return (
<Checkbox
@ -113,6 +123,12 @@ class TicketList extends React.Component {
}
}
pageSizeChange(event) {
const { onPageSizeChange } = this.props;
onPageSizeChange && onPageSizeChange(event.pageSize);
}
getDepartmentDropdownProps() {
const { departments, onDepartmentChange } = this.props;
@ -138,7 +154,7 @@ class TicketList extends React.Component {
loading,
headers: this.getTableHeaders(),
rows: this.getTableRows(),
pageSize: 10,
pageSize: this.state.tickets,
page,
pages,
onPageChange

View File

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

View File

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

View File

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

View File

@ -20,10 +20,11 @@ class AdminPanelNewTickets extends React.Component {
state = {
departmentId: null,
pageSize: 10
};
componentDidMount() {
this.retrieveNewTickets();
this.retrieveNewTickets({});
}
render() {
@ -32,7 +33,7 @@ class AdminPanelNewTickets extends React.Component {
<div className="admin-panel-new-tickets">
<Header title={i18n('NEW_TICKETS')} description={i18n('NEW_TICKETS_DESCRIPTION')} />
{(noDepartments) ? <Message showCloseButton={false} className="admin-panel-new-tickets__department-warning" type="warning">{i18n('NO_DEPARTMENT_ASSIGNED')}</Message> : null}
{(this.props.error) ? <Message showCloseButton={false} type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()}/>}
{(this.props.error) ? <Message showCloseButton={false} type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()} />}
</div>
);
}
@ -47,16 +48,20 @@ class AdminPanelNewTickets extends React.Component {
ticketPath: '/admin/panel/tickets/view-ticket/',
page: this.props.page,
pages: this.props.pages,
onPageChange: event => this.retrieveNewTickets(event.target.value),
onPageChange: event => this.retrieveNewTickets({page: event.target.value}),
onDepartmentChange: 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) {
this.props.dispatch(AdminDataAction.retrieveNewTickets(page, departmentId));
retrieveNewTickets({page = this.props.page, departmentId = this.state.departmentId, pageSize = this.state.pageSize }) {
this.props.dispatch(AdminDataAction.retrieveNewTickets({page, departmentId, pageSize}));
}
}

View File

@ -38,7 +38,8 @@ export function updateSearchTicketsFromURL() {
...store.getState().searchFilters.ticketQueryListState,
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',
'SUPERVISOR_CAN_NOT_SUPERVISE_HIMSELF': 'Supervisor can not supervise himself',
'NAME_ALREADY_USED': 'Name already used',
'PAGESIZE_ERROR': 'Invalid page size',
//MESSAGES
'SIGNUP_SUCCESS': 'You have registered successfully in our support system.',

View File

@ -38,6 +38,10 @@ class GetNewTicketsStaffController extends Controller {
'page' => [
'validation' => DataValidator::numeric(),
'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() {
$page = Controller::request('page');
$departmentId = Controller::request('departmentId');
$pageSize = Controller::request('pageSize');
if (Ticket::isTableEmpty()) {
Response::respondSuccess([
@ -77,14 +82,14 @@ class GetNewTicketsStaffController extends Controller {
$countTotal = Ticket::count($query);
$query .= ' ORDER BY unread_staff DESC';
$query .= ' LIMIT 10 OFFSET ' . ($page-1)*10;
$query .= ' LIMIT ' . $pageSize . ' OFFSET ' . ($page-1)*10;
$ticketList = Ticket::find($query);
Response::respondSuccess([
'tickets' => $ticketList->toArray(true),
'page' => $page,
'pages' => ceil($countTotal / 10)
'pages' => ceil($countTotal / $pageSize)
]);
}
}

View File

@ -38,6 +38,10 @@ class GetTicketStaffController extends Controller {
'page' => [
'validation' => DataValidator::numeric(),
'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');
$page = Controller::request('page');
$departmentId = Controller::request('departmentId');
$offset = ($page-1)*10;
$pageSize = Controller::request('pageSize');
$offset = ($page-1)*$pageSize;
$condition = 'TRUE';
$bindings = [];
@ -65,7 +70,7 @@ class GetTicketStaffController extends Controller {
$countTotal = $user->withCondition($condition, $bindings)->countShared('ticket');
$condition .= ' LIMIT 10 OFFSET ?';
$condition .= ' LIMIT ' . $pageSize . ' OFFSET ?';
$bindings[] = $offset;
$tickets = $user->withCondition($condition, $bindings)->sharedTicketList->toArray(true);
@ -73,7 +78,7 @@ class GetTicketStaffController extends Controller {
Response::respondSuccess([
'tickets' => $tickets,
'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()),
'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),
'page' => Controller::request('page'),
'allowedDepartments' => $allowedDepartmentsId,
'staffId' => Controller::getLoggedUser()->id
'staffId' => Controller::getLoggedUser()->id,
'pageSize' => Controller::request('pageSize')
];
$query = $this->getSQLQuery($inputs);
$queryWithOrder = $this->getSQLQueryWithOrder($inputs, $query);
@ -137,7 +142,7 @@ class SearchController extends Controller {
}
Response::respondSuccess([
'tickets' => $ticketList,
'pages' => ceil($totalCount / 10),
'pages' => ceil($totalCount / $inputs['pageSize']),
'page' => $inputs['page'] ? ($inputs['page']*1) : 1
]);
}
@ -154,12 +159,14 @@ class SearchController extends Controller {
}
public function getSQLQueryWithOrder($inputs, $query) {
$pageSize = $inputs['pageSize'];
$order = "";
$query = "SELECT ticket.id " . $query;
$this->setQueryOrder($inputs, $order);
$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;
}

View File

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