Fix bug in ticket list in my account and view staff (#921)

* wip

* Delete ticket of staff/get.php response

* Move paginate functions to staff editor component

* wip

* fix assignedTickets and closedTickets values in staff/get-all

* add show closed tickets checkbox in admin panel my account

* WIP

* Add initial api value

* Fix staff/get-all test

* WIP
This commit is contained in:
LautaroCesso 2020-11-13 14:23:14 -03:00 committed by GitHub
parent 9feb7d6cd4
commit 15f765cf85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 204 additions and 51 deletions

View File

@ -3,7 +3,6 @@ import {connect} from 'react-redux';
import _ from 'lodash';
import i18n from 'lib-app/i18n';
import API from 'lib-app/api-call';
import SessionActions from 'actions/session-actions';
import StaffEditor from 'app/admin/panel/staff/staff-editor';

View File

@ -67,6 +67,7 @@ class AdminPanelViewStaff extends React.Component {
params,
dispatch
} = this.props;
this.setState({
loading: false,
userData: result.data

View File

@ -20,6 +20,12 @@ import Button from 'core-components/button';
import Icon from 'core-components/icon';
import Loading from 'core-components/loading';
const INITIAL_API_VALUE = {
page: 1,
closed: 0,
departments: undefined,
};
class StaffEditor extends React.Component {
static propTypes = {
myAccount: React.PropTypes.bool,
@ -29,41 +35,49 @@ class StaffEditor extends React.Component {
name: React.PropTypes.string.isRequired,
profilePic: React.PropTypes.string.isRequired,
level: React.PropTypes.number.isRequired,
tickets: React.PropTypes.array.isRequired,
departments: React.PropTypes.array.isRequired,
sendEmailOnNewTicket: React.PropTypes.bool,
onChange: React.PropTypes.func,
onDelete: React.PropTypes.func
};
static defaultProps = {
tickets: []
};
state = {
email: this.props.email,
level: this.props.level - 1,
message: null,
loadingPicture: false,
tickets: [],
page: 1,
pages: 0,
department: undefined,
departments: this.getUserDepartments(),
closedTicketsShown: false,
sendEmailOnNewTicket: this.props.sendEmailOnNewTicket
};
componentDidMount() {
this.retrieveStaffMembers();
this.retrieveTicketsAssigned(INITIAL_API_VALUE);
}
render() {
const {
name,
level,
tickets,
profilePic,
myAccount,
staffId,
staffList,
userId
} = this.props;
const {
message,
tickets,
loadingPicture,
email
} = this.state;
const myData = _.filter(staffList, {id: `${staffId}`})[0];
return (
<div className="staff-editor">
{message ? this.renderMessage() : null}
@ -83,13 +97,13 @@ class StaffEditor extends React.Component {
</div>
<div className="staff-editor__card-badge">
<span className="staff-editor__card-badge-green">
{_.filter(tickets, {closed: false}).length}
{myData.assignedTickets}
</span>
<span className="staff-editor__card-badge-text">{i18n('ASSIGNED')}</span>
</div>
<div className="staff-editor__card-badge">
<span className="staff-editor__card-badge-red">
{_.filter(tickets, {closed: true}).length}
{myData.closedTickets}
</span>
<span className="staff-editor__card-badge-text">{i18n('CLOSED')}</span>
</div>
@ -98,21 +112,21 @@ class StaffEditor extends React.Component {
<label className={this.getPictureWrapperClass()}>
<div className="staff-editor__card-pic-background"></div>
<img className="staff-editor__card-pic" src={(profilePic) ? API.getFileLink(profilePic) : (API.getURL() + '/images/profile.png')} />
{(loadingPicture) ? <Loading className="staff-editor__card-pic-loading" size="large"/> : <Icon className="staff-editor__card-pic-icon" name="upload" size="4x"/>}
<input className="staff-editor__image-uploader" type="file" multiple={false} accept="image/x-png,image/gif,image/jpeg" onChange={this.onProfilePicChange.bind(this)}/>
{(loadingPicture) ? <Loading className="staff-editor__card-pic-loading" size="large" /> : <Icon className="staff-editor__card-pic-icon" name="upload" size="4x" />}
<input className="staff-editor__image-uploader" type="file" multiple={false} accept="image/x-png,image/gif,image/jpeg" onChange={this.onProfilePicChange.bind(this)} />
</label>
</div>
</div>
<div className="col-md-8">
<div className="staff-editor__form">
<Form className="staff-editor__update-email" values={{email: email}} onChange={form => this.setState({email: form.email})} onSubmit={this.onSubmit.bind(this, 'EMAIL')}>
<FormField name="email" validation="EMAIL" required label={i18n('EMAIL')} fieldProps={{size: 'large'}}/>
<FormField name="email" validation="EMAIL" required label={i18n('EMAIL')} fieldProps={{size: 'large'}} />
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE_EMAIL')}</SubmitButton>
</Form>
<span className="separator staff-editor__separator" />
<Form className="staff-editor__update-password" onSubmit={this.onSubmit.bind(this, 'PASSWORD')}>
<FormField name="password" validation="PASSWORD" required label={i18n('PASSWORD')} fieldProps={{size: 'large', password: true}}/>
<FormField name="rpassword" validation="REPEAT_PASSWORD" required label={i18n('REPEAT_PASSWORD')} fieldProps={{size: 'large', password: true}}/>
<FormField name="password" validation="PASSWORD" required label={i18n('PASSWORD')} fieldProps={{size: 'large', password: true}} />
<FormField name="rpassword" validation="REPEAT_PASSWORD" required label={i18n('REPEAT_PASSWORD')} fieldProps={{size: 'large', password: true}} />
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE_PASSWORD')}</SubmitButton>
</Form>
{(myAccount) ? this.renderSendEmailOnNewTicketForm() : this.renderLevelForm()}
@ -124,7 +138,7 @@ class StaffEditor extends React.Component {
<div className="col-md-4">
<div className="staff-editor__departments">
<div className="staff-editor__departments-title">{i18n('DEPARTMENTS')}</div>
{(myAccount && (level !== 3)) ? this.renderDepartmentsInfo() : this.renderDepartmentsForm()}
{(myAccount && (level !== 3)) ? this.renderDepartmentsInfo() : this.renderDepartmentsForm()}
</div>
</div>
<div className="col-md-8">
@ -172,7 +186,7 @@ class StaffEditor extends React.Component {
renderSendEmailOnNewTicketForm() {
return (
<div>
<span className="separator staff-editor__separator"/>
<span className="separator staff-editor__separator" />
<Form className="staff-editor__update-email-setting" values={{sendEmailOnNewTicket: this.state.sendEmailOnNewTicket}} onChange={form => this.setState({sendEmailOnNewTicket: form.sendEmailOnNewTicket})} onSubmit={this.onSubmit.bind(this, 'SEND_EMAIL_ON_NEW_TICKET')}>
<FormField name="sendEmailOnNewTicket" label={i18n('SEND_EMAIL_ON_NEW_TICKET')} field="checkbox" fieldProps={{size: 'large'}} />
<SubmitButton size="medium" className="staff-editor__submit-button">{i18n('UPDATE')}</SubmitButton>
@ -184,7 +198,7 @@ class StaffEditor extends React.Component {
renderLevelForm() {
return (
<div>
<span className="separator staff-editor__separator"/>
<span className="separator staff-editor__separator" />
<Form className="staff-editor__update-level" values={{level: this.state.level}} onChange={form => this.setState({level: form.level})} onSubmit={this.onSubmit.bind(this, 'LEVEL')}>
<FormField name="level" label={i18n('LEVEL')} field="select" infoMessage={this.getStaffLevelInfo()} fieldProps={{
items: [{content: i18n('LEVEL_1')}, {content: i18n('LEVEL_2')}, {content: i18n('LEVEL_3')}],
@ -216,7 +230,7 @@ class StaffEditor extends React.Component {
renderStaffStats() {
// return (
// <Stats staffId={this.props.staffId} type="staff"/>
// <Stats staffId={this.props.staffId} type="staff" />
// );
return null;
@ -225,10 +239,10 @@ class StaffEditor extends React.Component {
renderTickets() {
return (
<div>
<span className="separator"/>
<span className="separator" />
<div className="staff-editor__tickets">
<div className="staff-editor__tickets-title">{i18n('TICKETS')}</div>
<TicketList {...this.getTicketListProps()}/>
<div className="staff-editor__tickets-title">{i18n('TICKETS_ASSIGNED')}</div>
<TicketList {...this.getTicketListProps()} />
</div>
</div>
);
@ -237,7 +251,7 @@ class StaffEditor extends React.Component {
renderDelete() {
return (
<div>
<span className="separator"/>
<span className="separator" />
<div className="staff-editor__delete">
<div className="staff-editor__delete-title">
{i18n('DELETE_STAFF_MEMBER')}
@ -262,15 +276,27 @@ class StaffEditor extends React.Component {
getTicketListProps() {
const {
staffId,
tickets,
departments
} = this.props;
const {
tickets,
page,
pages,
closedTicketsShown
} = this.state;
return {
type: 'secondary',
userId: staffId,
tickets: tickets,
departments: departments,
ticketPath: '/admin/panel/tickets/view-ticket/'
tickets,
departments,
closedTicketsShown,
ticketPath: '/admin/panel/tickets/view-ticket/',
page,
pages,
onPageChange: this.onPageChange.bind(this),
onDepartmentChange: this.onDepartmentChange.bind(this),
onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this)
};
}
@ -289,7 +315,7 @@ class StaffEditor extends React.Component {
getDepartments() {
return SessionStore.getDepartments().map(department => {
if(department.private*1){
return <span> {department.name} <Icon name='user-secret'/> </span>
return <span> {department.name} <Icon name='user-secret' /> </span>
} else {
return department.name;
}
@ -337,7 +363,7 @@ class StaffEditor extends React.Component {
email: (eventType === 'EMAIL') ? form.email : null,
password: (eventType === 'PASSWORD') ? form.password : null,
level: ((form.level !== undefined) && (eventType == 'LEVEL')) ? form.level + 1 : null,
departments: (eventType === 'DEPARTMENTS') ? (departments && JSON.stringify(departments)) : null,
departments: (eventType === 'DEPARTMENTS') ? (departments && JSON.stringify(departments)) : null
}
}).then(() => {
this.retrieveStaffMembers();
@ -396,9 +422,78 @@ class StaffEditor extends React.Component {
});
}
retrieveTicketsAssigned({page, department, closed}) {
API.call({
path: '/ticket/search',
data: {
page,
departments: department,
closed,
owners: `[${this.props.staffId}]`
}
}).then((result) => {
const data = result.data;
this.setState({
tickets: data.tickets,
page: data.page,
pages: data.pages
});
});
}
onPageChange(event) {
this.setState({
page: event.target.value
});
this.retrieveTicketsAssigned({page: event.target.value});
}
onDepartmentChange(department) {
const { closedTicketsShown } = this.state;
this.setState({
department
});
this.retrieveTicketsAssigned(this.prepareFiltersForAPI({
newClosedFilter: closedTicketsShown,
newDepartmentFilter: department
}));
}
onClosedTicketsShownChange() {
const {
department,
closedTicketsShown
} = this.state;
const newClosedValue = !closedTicketsShown;
this.setState({
closedTicketsShown: newClosedValue
});
this.retrieveTicketsAssigned(this.prepareFiltersForAPI({
newClosedFilter: newClosedValue,
newDepartmentFilter: department
}));
}
retrieveStaffMembers() {
this.props.dispatch(AdminDataActions.retrieveStaffMembers());
}
prepareFiltersForAPI({newClosedFilter, newDepartmentFilter}) {
return {
closed: newClosedFilter ? undefined : 0,
department: newDepartmentFilter ? `[${newDepartmentFilter}]` : undefined
}
}
}
export default connect()(StaffEditor);
export default connect((store) => {
return {
staffList: store.adminData.staffMembers
};
})(StaffEditor);

View File

@ -121,6 +121,7 @@ export default {
'DELETE_AND_BAN': 'Delete and ban',
'STAFF_LEVEL': 'Staff Level',
'ASSIGNED': 'Assigned',
'TICKETS_ASSIGNED': 'Tickets assigned',
'ASSIGNED_TICKETS': '{tickets} assigned tickets',
'CLOSED_TICKETS': '{tickets} closed tickets',
'LAST_LOGIN': 'Last login',

View File

@ -40,8 +40,8 @@ class GetAllStaffController extends Controller {
$closedTickets = 0;
foreach ($staff->sharedTicketList as $ticket) {
if($ticket->closed) $closedTickets++;
else $assignedTickets++;
if(($ticket->closed) && ($ticket->owner_id == $staff->id)) $closedTickets++;
if($ticket->owner_id == $staff->id) $assignedTickets++;
}
$staffArray[] = [

View File

@ -25,7 +25,6 @@ DataValidator::with('CustomValidations', true);
* @apiSuccess {Number} data.level Level of staff member
* @apiSuccess {Boolean} data.staff Indicates that it is a staff (always true)
* @apiSuccess {[Department](#api-Data_Structures-ObjectDepartment)[]} data.departments Array of departments that has assigned.
* @apiSuccess {[Ticket](#api-Data_Structures-ObjectTicket)[]} data.tickets Array of tickets that has assigned.
* @apiSuccess {Boolean} data.sendEmailOnNewTicket Indicates if this member receives a mail when a ticket is created.
*
*/
@ -69,7 +68,6 @@ class GetStaffController extends Controller {
'level' => $user->level,
'staff' => true,
'departments' => $parsedDepartmentList,
'tickets' => $user->sharedTicketList->toArray(true),
'sendEmailOnNewTicket' => $user->sendEmailOnNewTicket
]);
}

View File

@ -2,6 +2,25 @@ describe '/staff/assign-ticket' do
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
Scripts.createTicket('ticket_to_assing_1')
Scripts.createTicket('ticket_to_assing_2')
Scripts.createTicket('ticket_to_assing_3')
Scripts.createTicket('ticket_to_assing_4')
Scripts.createTicket('ticket_to_assing_5')
Scripts.createTicket('ticket_to_assing_6')
Scripts.createTicket('ticket_to_assing_7')
Scripts.createTicket('ticket_to_assing_8')
Scripts.createTicket('ticket_to_assing_9')
Scripts.createTicket('ticket_to_assing_10')
Scripts.createTicket('ticket_to_assing_11')
Scripts.createTicket('ticket_to_assing_12')
Scripts.createTicket('ticket_to_assing_13')
Scripts.createTicket('ticket_to_assing_14')
Scripts.createTicket('ticket_to_assing_15')
Scripts.createTicket('ticket_to_assing_16')
Scripts.createTicket('ticket_to_assing_17')
Scripts.createTicket('ticket_to_assing_18')
#TODO: Create a staff without all department
#it 'should fail if staff is not in the same department'do
@ -9,26 +28,66 @@ describe '/staff/assign-ticket' do
#end
it 'should assign ticket if everything is okey' do
ticket = $database.getRow('ticket', 1 , 'id')
result = request('/staff/assign-ticket', {
ticketNumber: ticket['ticket_number'],
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
ticket1 = $database.getRow('ticket', 'ticket_to_assing_1', 'title')
ticket2 = $database.getRow('ticket', 'ticket_to_assing_2', 'title')
ticket3 = $database.getRow('ticket', 'ticket_to_assing_3', 'title')
ticket4 = $database.getRow('ticket', 'ticket_to_assing_4', 'title')
ticket5 = $database.getRow('ticket', 'ticket_to_assing_5', 'title')
ticket6 = $database.getRow('ticket', 'ticket_to_assing_6', 'title')
ticket7 = $database.getRow('ticket', 'ticket_to_assing_7', 'title')
ticket8 = $database.getRow('ticket', 'ticket_to_assing_8', 'title')
ticket9 = $database.getRow('ticket', 'ticket_to_assing_9', 'title')
ticket10 = $database.getRow('ticket', 'ticket_to_assing_10', 'title')
ticket11 = $database.getRow('ticket', 'ticket_to_assing_11', 'title')
ticket12 = $database.getRow('ticket', 'ticket_to_assing_12', 'title')
ticket13 = $database.getRow('ticket', 'ticket_to_assing_13', 'title')
ticket14 = $database.getRow('ticket', 'ticket_to_assing_14', 'title')
ticket15 = $database.getRow('ticket', 'ticket_to_assing_15', 'title')
ticket16 = $database.getRow('ticket', 'ticket_to_assing_16', 'title')
ticket17 = $database.getRow('ticket', 'ticket_to_assing_17', 'title')
ticket18 = $database.getRow('ticket', 'ticket_to_assing_18', 'title')
ticket_with_id_1 = $database.getRow('ticket', 1 , 'id')
(result['status']).should.equal('success')
tickets = [
ticket1,
ticket2,
ticket3,
ticket4,
ticket5,
ticket6,
ticket7,
ticket8,
ticket9,
ticket10,
ticket11,
ticket12,
ticket13,
ticket14,
ticket15,
ticket16,
ticket17,
ticket18,
ticket_with_id_1
]
ticket = $database.getRow('ticket', 1 , 'id')
for ticket in tickets
result = request('/staff/assign-ticket', {
ticketNumber: ticket['ticket_number'],
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
(ticket['owner_id']).should.equal(1)
(result['status']).should.equal('success')
(ticket['unread']).should.equal(1)
ticket = $database.getRow('ticket', ticket['ticket_number'], 'ticket_number')
staff_ticket = $database.getRow('staff_ticket', 1 , 'ticket_id')
(ticket['owner_id']).should.equal(1)
(staff_ticket['staff_id']).should.equal(1)
staff_ticket = $database.getRow('staff_ticket', ticket['id'].to_i , 'ticket_id')
(staff_ticket['ticket_id']).should.equal(1)
(staff_ticket['staff_id']).should.equal(1)
(staff_ticket['ticket_id']).should.equal(ticket['id'])
end
end
it 'should assign ticket if a staff choose another to assing a ticket ' do
staffId = $database.getRow('staff','ayra2@opensupports.com','email')['id']

View File

@ -46,7 +46,7 @@ describe 'Retrieve all tickets' do
})
(response['status']).should.equal('success')
(response['data']['pages']).should.equal(5)
(response['data']['pages']).should.equal(7)
(response['data']['tickets'].size).should.equal(10)
(response['data']['tickets'][0]['title']).should.equal('Quisque egestas ipsum')
(response['data']['tickets'][1]['title']).should.equal('placerat id velit')
@ -68,7 +68,7 @@ describe 'Retrieve all tickets' do
})
(response['status']).should.equal('success')
(response['data']['pages']).should.equal(5)
(response['data']['pages']).should.equal(7)
(response['data']['tickets'].size).should.equal(10)
(response['data']['tickets'][0]['title']).should.equal('quis vulputate lectus feugiat eu')
(response['data']['tickets'][1]['title']).should.equal('Fusce venenatis iaculis commodo')

View File

@ -24,8 +24,8 @@ describe'/staff/get-all' do
(result['data'][0]['departments'][1]['name']).should.equal('useless private deapartment')
(result['data'][0]['departments'][2]['id']).should.equal('3')
(result['data'][0]['departments'][2]['name']).should.equal('Suggestions')
(result['data'][0]['assignedTickets']).should.equal(10)
(result['data'][0]['closedTickets']).should.equal(1)
(result['data'][0]['assignedTickets']).should.equal(22)
(result['data'][0]['closedTickets']).should.equal(0)
(result['data'][2]['name']).should.equal('Arya Stark')
(result['data'][2]['email']).should.equal('ayra2@opensupports.com')

View File

@ -25,6 +25,6 @@ describe '/staff/get-tickets' do
})
(result['status']).should.equal('success')
(result['data']['tickets'].size).should.equal(9)
(result['data']['tickets'].size).should.equal(10)
end
end