Merge branch 'master' into 114-staff-member-view
Conflicts: client/src/data/languages/en.js
This commit is contained in:
commit
adfb1c7335
|
@ -0,0 +1,117 @@
|
||||||
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import {StaggeredMotion, spring} from 'react-motion';
|
||||||
|
import Menu from 'core-components/menu'
|
||||||
|
|
||||||
|
import DateTransformer from 'lib-core/date-transformer';
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
|
class PeopleList extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
list: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||||
|
profilePic: React.PropTypes.string,
|
||||||
|
name: React.PropTypes.string,
|
||||||
|
assignedTickets: React.PropTypes.number,
|
||||||
|
closedTickets: React.PropTypes.number,
|
||||||
|
lastLogin: React.PropTypes.number
|
||||||
|
})),
|
||||||
|
pageSize: React.PropTypes.number,
|
||||||
|
page: React.PropTypes.number,
|
||||||
|
onPageSelect: React.PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
pageSize: 4,
|
||||||
|
list: []
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const pages = _.range(1, this.getPages() + 1).map((index) => {return {content: index};});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="people-list">
|
||||||
|
<div className="people-list__list">
|
||||||
|
<StaggeredMotion defaultStyles={this.getDefaultStyles()} styles={this.getStyles.bind(this)}>
|
||||||
|
{this.renderList.bind(this)}
|
||||||
|
</StaggeredMotion>
|
||||||
|
</div>
|
||||||
|
<div className="people-list__pagination">
|
||||||
|
<Menu type="navigation" items={pages} selectedIndex={this.props.page - 1} onItemClick={this.props.onPageSelect} tabbable/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultStyles() {
|
||||||
|
return _.times(this.props.pageSize).map(() => {return {offset: -100, alpha: 0}});
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyles(prevStyles) {
|
||||||
|
return prevStyles.map((_, i) => {
|
||||||
|
return i === 0
|
||||||
|
? {offset: spring(0), alpha: spring(1)}
|
||||||
|
: {offset: spring(prevStyles[i - 1].offset), alpha: spring(prevStyles[i - 1].alpha)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderList(styles) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{styles.map(this.renderAnimatedItem.bind(this))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAnimatedItem(style, index) {
|
||||||
|
return (
|
||||||
|
<div style={{transform: 'translateX('+style.offset+'px)', opacity: style.alpha}} key={index}>
|
||||||
|
{this.renderItem(index + this.props.pageSize * (this.props.page - 1))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItem(index) {
|
||||||
|
if(index >= this.props.list.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = this.props.list[index];
|
||||||
|
const minIndex = this.props.pageSize * (this.props.page - 1);
|
||||||
|
const maxIndex = this.props.pageSize * this.props.page;
|
||||||
|
|
||||||
|
return (minIndex <= index && index < maxIndex) ? (
|
||||||
|
<div className="people-list__item">
|
||||||
|
<div className="people-list__item-profile-pic-wrapper">
|
||||||
|
<img className="people-list__item-profile-pic" src={item.profilePic} />
|
||||||
|
</div>
|
||||||
|
<div className="people-list__item-block people-list__item-name">{item.name}</div>
|
||||||
|
<div className="people-list__item-block people-list__item-assigned-tickets">
|
||||||
|
{i18n('ASSIGNED_TICKETS', {tickets: item.assignedTickets})}
|
||||||
|
</div>
|
||||||
|
<div className="people-list__item-block people-list__item-closed-tickets">
|
||||||
|
{i18n('CLOSED_TICKETS', {tickets: item.closedTickets})}
|
||||||
|
</div>
|
||||||
|
<div className="people-list__item-block people-list__item-last-login">
|
||||||
|
<div>{i18n('LAST_LOGIN')}</div>
|
||||||
|
<div>{DateTransformer.transformToString(item.lastLogin)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowsQuantity() {
|
||||||
|
if(this.props.page == this.getPages()){
|
||||||
|
return this.props.list.length % this.props.pageSize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.props.pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPages() {
|
||||||
|
return Math.ceil(this.props.list.length / this.props.pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PeopleList;
|
|
@ -0,0 +1,64 @@
|
||||||
|
@import "../scss/vars";
|
||||||
|
|
||||||
|
.people-list {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
border: 2px solid $grey;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
height: 105px;
|
||||||
|
padding-left: 60px;
|
||||||
|
font-size: $font-size--md;
|
||||||
|
|
||||||
|
&-profile-pic-wrapper {
|
||||||
|
vertical-align: top;
|
||||||
|
background-color: $secondary-blue;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
border: 2px solid $light-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-profile-pic {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
&-block {
|
||||||
|
padding: 30px 0;
|
||||||
|
width: 25%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-assigned-tickets {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&-closed-tickets {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&-last-login {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__pagination {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
import SessionStore from 'lib-app/session-store';
|
||||||
|
|
||||||
|
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 Button from 'core-components/button';
|
||||||
|
|
||||||
|
class AddStaffModal extends React.Component {
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
closeModal: React.PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
errors: {},
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="add-staff-modal">
|
||||||
|
<Header title={i18n('ADD_STAFF')} description={i18n('ADD_STAFF_DESCRIPTION')} />
|
||||||
|
<Form onSubmit={this.onSubmit.bind(this)} errors={this.getErrors()} onValidateErrors={errors => this.setState({errors})} loading={this.state.loading}>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-7">
|
||||||
|
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} validation="NAME" required />
|
||||||
|
<FormField name="email" label={i18n('EMAIL')} fieldProps={{size: 'large'}} validation="EMAIL" required />
|
||||||
|
<FormField name="password" label={i18n('PASSWORD')} fieldProps={{size: 'large', password: true}} validation="PASSWORD" required />
|
||||||
|
<div className="add-staff-modal__level-selector">
|
||||||
|
<FormField name="level" label={i18n('LEVEL')} field="select" fieldProps={{
|
||||||
|
items: [{content: i18n('LEVEL_1')}, {content: i18n('LEVEL_2')}, {content: i18n('LEVEL_3')}],
|
||||||
|
size: 'large'
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-5">
|
||||||
|
<div className="add-staff-modal__departments">
|
||||||
|
<div className="add-staff-modal__departments-title">{i18n('Departments')}</div>
|
||||||
|
<FormField name="departments" field="checkbox-group" fieldProps={{items: this.getDepartments()}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SubmitButton type="secondary" size="small">
|
||||||
|
{i18n('SAVE')}
|
||||||
|
</SubmitButton>
|
||||||
|
<Button type="clean" onClick={this.onCancelClick.bind(this)}>
|
||||||
|
{i18n('CANCEL')}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepartments() {
|
||||||
|
return SessionStore.getDepartments().map(department => department.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(form) {
|
||||||
|
let departments = _.filter(SessionStore.getDepartments(), (department, index) => {
|
||||||
|
return _.includes(form.departments, index);
|
||||||
|
}).map(department => department.id);
|
||||||
|
|
||||||
|
this.setState({loading: true});
|
||||||
|
|
||||||
|
API.call({
|
||||||
|
path: '/staff/add',
|
||||||
|
data: {
|
||||||
|
name: form.name,
|
||||||
|
email: form.email,
|
||||||
|
password: form.password,
|
||||||
|
level: form.level + 1,
|
||||||
|
department: JSON.stringify(departments)
|
||||||
|
}
|
||||||
|
}).then(this.context.closeModal).catch((result) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: result.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancelClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.context.closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
getErrors() {
|
||||||
|
let errors = _.extend({}, this.state.errors);
|
||||||
|
|
||||||
|
if (this.state.error === 'ALREADY_A_STAFF') {
|
||||||
|
errors.email = i18n('EMAIL_EXISTS');
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddStaffModal;
|
|
@ -0,0 +1,23 @@
|
||||||
|
@import "../../../../scss/vars";
|
||||||
|
|
||||||
|
.add-staff-modal {
|
||||||
|
width: 700px;
|
||||||
|
|
||||||
|
&__level-selector {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__departments {
|
||||||
|
@include scrollbars();
|
||||||
|
|
||||||
|
border: 1px solid $grey;
|
||||||
|
padding: 20px;
|
||||||
|
height: 320px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__departments-title {
|
||||||
|
font-size: $font-size--md;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,99 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
import SessionStore from 'lib-app/session-store';
|
||||||
|
import PeopleList from 'app-components/people-list';
|
||||||
|
import ModalContainer from 'app-components/modal-container';
|
||||||
|
|
||||||
|
import AddStaffModal from 'app/admin/panel/staff/add-staff-modal';
|
||||||
|
|
||||||
|
import Header from 'core-components/header';
|
||||||
|
import DropDown from 'core-components/drop-down';
|
||||||
|
import Button from 'core-components/button';
|
||||||
|
import Icon from 'core-components/icon';
|
||||||
|
import Loading from 'core-components/loading';
|
||||||
|
|
||||||
class AdminPanelStaffMembers extends React.Component {
|
class AdminPanelStaffMembers extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
selectedDepartment: 0,
|
||||||
|
staffList: [],
|
||||||
|
loading: true,
|
||||||
|
page: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
API.call({
|
||||||
|
path: '/staff/get-all',
|
||||||
|
data: {}
|
||||||
|
}).then(this.onStaffRetrieved.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="admin-panel-staff-members">
|
||||||
/admin/panel/staff/staff-members
|
<Header title={i18n('STAFF_MEMBERS')} description={i18n('STAFF_MEMBERS_DESCRIPTION')} />
|
||||||
|
<div className="admin-panel-staff-members__wrapper">
|
||||||
|
<DropDown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
||||||
|
<Button onClick={this.onAddNewStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||||
|
<Icon name="user-plus" className=""/> {i18n('ADD_NEW_STAFF')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{(this.state.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAddNewStaff() {
|
||||||
|
ModalContainer.openModal(<AddStaffModal />);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepartmentDropdownProps() {
|
||||||
|
return {
|
||||||
|
items: this.getDepartments(),
|
||||||
|
onChange: (event) => {
|
||||||
|
let departments = SessionStore.getDepartments();
|
||||||
|
this.setState({
|
||||||
|
selectedDepartment: event.index && departments[event.index - 1].id,
|
||||||
|
page: 1
|
||||||
|
});
|
||||||
|
},
|
||||||
|
size: 'medium'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getStaffList() {
|
||||||
|
if(!this.state.selectedDepartment) {
|
||||||
|
return this.state.staffList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.filter(this.state.staffList, (o) => {
|
||||||
|
return _.findIndex(o.departments, {id: this.state.selectedDepartment}) !== -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepartments() {
|
||||||
|
let departments = SessionStore.getDepartments().map((department) => {
|
||||||
|
return {content: department.name};
|
||||||
|
});
|
||||||
|
|
||||||
|
departments.unshift({
|
||||||
|
content: i18n('ALL_DEPARTMENTS')
|
||||||
|
});
|
||||||
|
|
||||||
|
return departments;
|
||||||
|
}
|
||||||
|
|
||||||
|
onStaffRetrieved(result) {
|
||||||
|
if(result.status == 'success'){
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
staffList: result.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminPanelStaffMembers;
|
export default AdminPanelStaffMembers;
|
|
@ -0,0 +1,17 @@
|
||||||
|
@import "../../../../scss/vars";
|
||||||
|
|
||||||
|
.admin-panel-staff-members {
|
||||||
|
|
||||||
|
&__wrapper {
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dropdown {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
float: right;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import Checkbox from 'core-components/checkbox';
|
||||||
|
|
||||||
|
class CheckboxGroup extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
items: React.PropTypes.array.isRequired,
|
||||||
|
value: React.PropTypes.arrayOf(React.PropTypes.number),
|
||||||
|
onChange: React.PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
value: []
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ul className="checkbox-group">
|
||||||
|
{this.props.items.map(this.renderItem.bind(this))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItem(label, index) {
|
||||||
|
const checked = (_.includes(this.getValue(), index));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="checkbox-group__item" key={index}>
|
||||||
|
<Checkbox label={label} checked={checked} onChange={this.onCheckboxChange.bind(this, index)} wrapInLabel/>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCheckboxChange(index) {
|
||||||
|
let value = _.clone(this.getValue());
|
||||||
|
|
||||||
|
if(_.includes(value, index)) {
|
||||||
|
_.pull(value, index);
|
||||||
|
} else {
|
||||||
|
value.push(index);
|
||||||
|
value.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({value});
|
||||||
|
|
||||||
|
if(this.props.onChange) {
|
||||||
|
this.props.onChange({target: {value}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return (this.props.value !== undefined) ? this.props.value : this.state.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckboxGroup;
|
|
@ -0,0 +1,11 @@
|
||||||
|
@import "../scss/vars";
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,28 +11,38 @@ class CheckBox extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
alignment: React.PropTypes.string,
|
alignment: React.PropTypes.string,
|
||||||
label: React.PropTypes.string,
|
label: React.PropTypes.string,
|
||||||
value: React.PropTypes.bool
|
value: React.PropTypes.bool,
|
||||||
|
wrapInLabel: React.PropTypes.bool,
|
||||||
|
onChange: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
wrapInLabel: false,
|
||||||
alignment: 'right'
|
alignment: 'right'
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
state = {
|
||||||
super(props);
|
checked: false
|
||||||
|
};
|
||||||
this.state = {
|
|
||||||
checked: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let Wrapper = (this.props.wrapInLabel) ? 'label' : 'span';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={this.getClass()}>
|
<Wrapper className={this.getClass()}>
|
||||||
<span {...this.getIconProps()}>
|
<span {...this.getIconProps()}>
|
||||||
{getIcon((this.getValue()) ? 'check-square' : 'square', 'lg') }
|
{getIcon((this.getValue()) ? 'check-square' : 'square', 'lg') }
|
||||||
</span>
|
</span>
|
||||||
<input {...this.getProps()}/>
|
<input {...this.getProps()}/>
|
||||||
|
{(this.props.label) ? this.renderLabel() : null}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLabel() {
|
||||||
|
return (
|
||||||
|
<span className="checkbox__label">
|
||||||
|
{this.props.label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +53,7 @@ class CheckBox extends React.Component {
|
||||||
props.type = 'checkbox';
|
props.type = 'checkbox';
|
||||||
|
|
||||||
props['aria-hidden'] = true;
|
props['aria-hidden'] = true;
|
||||||
props.className = 'checkbox--box';
|
props.className = 'checkbox__box';
|
||||||
props.checked = this.getValue();
|
props.checked = this.getValue();
|
||||||
props.onChange = callback(this.handleChange.bind(this), this.props.onChange);
|
props.onChange = callback(this.handleChange.bind(this), this.props.onChange);
|
||||||
|
|
||||||
|
@ -69,7 +79,7 @@ class CheckBox extends React.Component {
|
||||||
getIconProps() {
|
getIconProps() {
|
||||||
return {
|
return {
|
||||||
'aria-checked': this.getValue(),
|
'aria-checked': this.getValue(),
|
||||||
className: 'checkbox--icon',
|
className: 'checkbox__icon',
|
||||||
onKeyDown: callback(this.handleIconKeyDown.bind(this), this.props.onKeyDown),
|
onKeyDown: callback(this.handleIconKeyDown.bind(this), this.props.onKeyDown),
|
||||||
role: "checkbox",
|
role: "checkbox",
|
||||||
tabIndex: 0
|
tabIndex: 0
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
&--box {
|
&__box {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--icon {
|
&__icon {
|
||||||
color: $light-grey;
|
color: $light-grey;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
|
@ -18,8 +18,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&_checked {
|
&_checked {
|
||||||
.checkbox--icon {
|
.checkbox__icon {
|
||||||
color: $primary-red;
|
color: $primary-red;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
|
|
@ -49,4 +49,17 @@
|
||||||
border: 1px solid $light-grey;
|
border: 1px solid $light-grey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_large {
|
||||||
|
width: 300px;
|
||||||
|
|
||||||
|
.drop-down__current-item {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop-down__list-container {
|
||||||
|
width: 300px;
|
||||||
|
border: 1px solid $light-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ import _ from 'lodash';
|
||||||
import Input from 'core-components/input';
|
import Input from 'core-components/input';
|
||||||
import DropDown from 'core-components/drop-down';
|
import DropDown from 'core-components/drop-down';
|
||||||
import Checkbox from 'core-components/checkbox';
|
import Checkbox from 'core-components/checkbox';
|
||||||
|
import CheckboxGroup from 'core-components/checkbox-group';
|
||||||
import TextEditor from 'core-components/text-editor';
|
import TextEditor from 'core-components/text-editor';
|
||||||
|
|
||||||
class FormField extends React.Component {
|
class FormField extends React.Component {
|
||||||
|
@ -21,7 +22,7 @@ class FormField extends React.Component {
|
||||||
required: React.PropTypes.bool,
|
required: React.PropTypes.bool,
|
||||||
error: React.PropTypes.string,
|
error: React.PropTypes.string,
|
||||||
value: React.PropTypes.any,
|
value: React.PropTypes.any,
|
||||||
field: React.PropTypes.oneOf(['input', 'textarea', 'select', 'checkbox']),
|
field: React.PropTypes.oneOf(['input', 'textarea', 'select', 'checkbox', 'checkbox-group']),
|
||||||
fieldProps: React.PropTypes.object
|
fieldProps: React.PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,6 +37,9 @@ class FormField extends React.Component {
|
||||||
else if (field === 'checkbox') {
|
else if (field === 'checkbox') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if (field === 'checkbox-group') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
else if (field === 'textarea') {
|
else if (field === 'textarea') {
|
||||||
return RichTextEditor.createEmptyValue();
|
return RichTextEditor.createEmptyValue();
|
||||||
}
|
}
|
||||||
|
@ -45,7 +49,7 @@ class FormField extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const Wrapper = (this.props.field === 'textarea') ? 'div' : 'label';
|
const Wrapper = (_.includes(this.getDivTypes(), this.props.field)) ? 'div' : 'label';
|
||||||
const fieldContent = [
|
const fieldContent = [
|
||||||
<span className="form-field__label" key="label">{this.props.label}</span>,
|
<span className="form-field__label" key="label">{this.props.label}</span>,
|
||||||
this.renderField(),
|
this.renderField(),
|
||||||
|
@ -67,7 +71,8 @@ class FormField extends React.Component {
|
||||||
'input': Input,
|
'input': Input,
|
||||||
'textarea': TextEditor,
|
'textarea': TextEditor,
|
||||||
'select': DropDown,
|
'select': DropDown,
|
||||||
'checkbox': Checkbox
|
'checkbox': Checkbox,
|
||||||
|
'checkbox-group': CheckboxGroup
|
||||||
}[this.props.field];
|
}[this.props.field];
|
||||||
|
|
||||||
if(this.props.decorator) {
|
if(this.props.decorator) {
|
||||||
|
@ -122,6 +127,13 @@ class FormField extends React.Component {
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDivTypes() {
|
||||||
|
return [
|
||||||
|
'textarea',
|
||||||
|
'checkbox-group'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
onChange(nativeEvent) {
|
onChange(nativeEvent) {
|
||||||
let event = nativeEvent;
|
let event = nativeEvent;
|
||||||
|
|
||||||
|
|
|
@ -536,6 +536,7 @@ module.exports = [
|
||||||
pages: 4
|
pages: 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -577,5 +578,68 @@ module.exports = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/staff/get-all',
|
||||||
|
time: 100,
|
||||||
|
response: function() {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
name: 'Emilia Clarke',
|
||||||
|
departments: [{id: 2, name: 'Technical issues'}],
|
||||||
|
assignedTickets: 4,
|
||||||
|
closedTickets: 21,
|
||||||
|
lastLogin: 20161212
|
||||||
|
},
|
||||||
|
{
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
name: 'Yulian A GUI Yermo',
|
||||||
|
departments: [{id: 2, name: 'Technical issues'}, {id: 1, name: 'Sales Support'}],
|
||||||
|
assignedTickets: 9,
|
||||||
|
closedTickets: 0,
|
||||||
|
lastLogin: 20161212
|
||||||
|
},
|
||||||
|
{
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
name: 'Miltona Costa',
|
||||||
|
departments: [{id: 1, name: 'Sales Support'}],
|
||||||
|
assignedTickets: -1,
|
||||||
|
closedTickets: -1,
|
||||||
|
lastLogin: 20160212
|
||||||
|
},
|
||||||
|
{
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
name: 'Emiliasnikova Rusachestkvuy',
|
||||||
|
departments: [{id: 1, name: 'Sales Support'}, {id: 3, name: 'System and Administration'}],
|
||||||
|
assignedTickets: 100,
|
||||||
|
closedTickets: 21,
|
||||||
|
lastLogin: 20130101
|
||||||
|
},
|
||||||
|
{
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
name: 'Laurita Morrechaga Rusachestkvuy',
|
||||||
|
departments: [{id: 3, name: 'System and Administration'}],
|
||||||
|
assignedTickets: 1,
|
||||||
|
closedTickets: 1,
|
||||||
|
lastLogin: 2012050
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/staff/add',
|
||||||
|
time: 100,
|
||||||
|
response: function () {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
staffId: 5
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
|
@ -96,6 +96,15 @@ export default {
|
||||||
'DELETE_AND_BAN': 'Delete and ban',
|
'DELETE_AND_BAN': 'Delete and ban',
|
||||||
'STAFF_LEVEL': 'Staff Level',
|
'STAFF_LEVEL': 'Staff Level',
|
||||||
'ASSIGNED': 'Assigned',
|
'ASSIGNED': 'Assigned',
|
||||||
|
'ASSIGNED_TICKETS': '{tickets} assigned tickets',
|
||||||
|
'CLOSED_TICKETS': '{tickets} closed tickets',
|
||||||
|
'LAST_LOGIN': 'Last login',
|
||||||
|
'ADD_NEW_STAFF': 'Add new staff',
|
||||||
|
'ADD_STAFF': 'Add staff',
|
||||||
|
'LEVEL': 'Level',
|
||||||
|
'LEVEL_1': 'Level 1 (Tickets)',
|
||||||
|
'LEVEL_2': 'Level 2 (Tickets + Articles)',
|
||||||
|
'LEVEL_3': 'Level 2 (Tickets + Articles + Staff)',
|
||||||
|
|
||||||
//VIEW DESCRIPTIONS
|
//VIEW DESCRIPTIONS
|
||||||
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',
|
'CREATE_TICKET_DESCRIPTION': 'This is a form for creating tickets. Fill the form and send us your issues/doubts/suggestions. Our support system will answer it as soon as possible.',
|
||||||
|
@ -118,6 +127,8 @@ export default {
|
||||||
'ADD_ARTICLE_DESCRIPTION': 'Here you can add an article that will be available for every user. It will be added inside the category {category}.',
|
'ADD_ARTICLE_DESCRIPTION': 'Here you can add an article that will be available for every user. It will be added inside the category {category}.',
|
||||||
'LIST_ARTICLES_DESCRIPTION': 'This is a list of articles that includes information about our services.',
|
'LIST_ARTICLES_DESCRIPTION': 'This is a list of articles that includes information about our services.',
|
||||||
'ADD_TOPIC_DESCRIPTION': 'Here you can add a topic that works as a category for articles.',
|
'ADD_TOPIC_DESCRIPTION': 'Here you can add a topic that works as a category for articles.',
|
||||||
|
'STAFF_MEMBERS_DESCRIPTION': 'Here you can see who are your staff members.',
|
||||||
|
'ADD_STAFF_DESCRIPTION': 'Here you can add staff members to your teams',
|
||||||
|
|
||||||
//ERRORS
|
//ERRORS
|
||||||
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
'EMAIL_OR_PASSWORD': 'Email or password invalid',
|
||||||
|
|
|
@ -25,4 +25,37 @@ $font-size--sm: 13px;
|
||||||
$font-size--md: 16px;
|
$font-size--md: 16px;
|
||||||
$font-size--bg: 19px;
|
$font-size--bg: 19px;
|
||||||
$font-size--lg: 24px;
|
$font-size--lg: 24px;
|
||||||
$font-size--xl: 32px;
|
$font-size--xl: 32px;
|
||||||
|
|
||||||
|
@mixin scrollbars() {
|
||||||
|
$size: .4em;
|
||||||
|
$color: $grey;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
backgroundr: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: $size;
|
||||||
|
height: $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: lighten($color, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@ require_once 'staff/get-tickets.php';
|
||||||
require_once 'staff/get-new-tickets.php';
|
require_once 'staff/get-new-tickets.php';
|
||||||
require_once 'staff/get-all-tickets.php';
|
require_once 'staff/get-all-tickets.php';
|
||||||
require_once 'staff/search-tickets.php';
|
require_once 'staff/search-tickets.php';
|
||||||
|
require_once 'staff/add.php';
|
||||||
|
require_once 'staff/get-all.php';
|
||||||
|
require_once 'staff/delete.php';
|
||||||
|
require_once 'staff/edit.php';
|
||||||
|
|
||||||
$systemControllerGroup = new ControllerGroup();
|
$systemControllerGroup = new ControllerGroup();
|
||||||
$systemControllerGroup->setGroupPath('/staff');
|
$systemControllerGroup->setGroupPath('/staff');
|
||||||
|
@ -17,5 +21,9 @@ $systemControllerGroup->addController(new GetTicketStaffController);
|
||||||
$systemControllerGroup->addController(new GetNewTicketsStaffController);
|
$systemControllerGroup->addController(new GetNewTicketsStaffController);
|
||||||
$systemControllerGroup->addController(new GetAllTicketsStaffController);
|
$systemControllerGroup->addController(new GetAllTicketsStaffController);
|
||||||
$systemControllerGroup->addController(new SearchTicketStaffController);
|
$systemControllerGroup->addController(new SearchTicketStaffController);
|
||||||
|
$systemControllerGroup->addController(new AddStaffController);
|
||||||
|
$systemControllerGroup->addController(new GetAllStaffController);
|
||||||
|
$systemControllerGroup->addController(new DeleteStaffController);
|
||||||
|
$systemControllerGroup->addController(new EditStaffController);
|
||||||
|
|
||||||
$systemControllerGroup->finalize();
|
$systemControllerGroup->finalize();
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
DataValidator::with('CustomValidations', true);
|
||||||
|
|
||||||
|
class AddStaffController extends Controller {
|
||||||
|
const PATH = '/add';
|
||||||
|
|
||||||
|
private $name;
|
||||||
|
private $email;
|
||||||
|
private $password;
|
||||||
|
private $profilePic;
|
||||||
|
private $level;
|
||||||
|
private $departments;
|
||||||
|
|
||||||
|
|
||||||
|
public function validations() {
|
||||||
|
return [
|
||||||
|
'permission' => 'staff_3',
|
||||||
|
'requestData' => [
|
||||||
|
'name' => [
|
||||||
|
'validation' => DataValidator::length(2, 55)->alpha(),
|
||||||
|
'error' => ERRORS::INVALID_NAME
|
||||||
|
],
|
||||||
|
'email' => [
|
||||||
|
'validation' => DataValidator::email(),
|
||||||
|
'error' => ERRORS::INVALID_EMAIL
|
||||||
|
],
|
||||||
|
'password' => [
|
||||||
|
'validation' => DataValidator::length(5, 200),
|
||||||
|
'error' => ERRORS::INVALID_PASSWORD
|
||||||
|
],
|
||||||
|
'level' => [
|
||||||
|
'validation' => DataValidator::between(1, 3, true),
|
||||||
|
'error' => ERRORS::INVALID_LEVEL
|
||||||
|
]
|
||||||
|
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handler() {
|
||||||
|
$this->storeRequestData();
|
||||||
|
$staff = new Staff();
|
||||||
|
|
||||||
|
$staffRow = Staff::getDataStore($this->email,'email');
|
||||||
|
|
||||||
|
if($staffRow->isNull()) {
|
||||||
|
$staff->setProperties([
|
||||||
|
'name'=> $this->name,
|
||||||
|
'email' => $this->email,
|
||||||
|
'password'=> Hashing::hashPassword($this->password),
|
||||||
|
'profilePic' => $this->profilePic,
|
||||||
|
'level' => $this->level,
|
||||||
|
'sharedDepartmentList'=> $this->getDepartmentList(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
Response::respondSuccess([
|
||||||
|
'id' => $staff->store()
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::respondError(ERRORS::ALREADY_A_STAFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeRequestData() {
|
||||||
|
$this->name = Controller::request('name');
|
||||||
|
$this->email = Controller::request('email');
|
||||||
|
$this->password = Controller::request('password');
|
||||||
|
$this->profilePic = Controller::request('profilePic');
|
||||||
|
$this->level = Controller::request('level');
|
||||||
|
$this->departments = Controller::request('departments');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDepartmentList() {
|
||||||
|
$listDepartments = new DataStoreList();
|
||||||
|
$departmentIds = json_decode($this->departments);
|
||||||
|
|
||||||
|
foreach($departmentIds as $id) {
|
||||||
|
$department = Department::getDataStore($id);
|
||||||
|
$listDepartments->add($department);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $listDepartments;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
DataValidator::with('CustomValidations', true);
|
||||||
|
|
||||||
|
class DeleteStaffController extends Controller {
|
||||||
|
const PATH = '/delete';
|
||||||
|
|
||||||
|
public function validations() {
|
||||||
|
return [
|
||||||
|
'permission' => 'staff_3',
|
||||||
|
'requestData' => [
|
||||||
|
'staffId' =>[
|
||||||
|
'validation' => DataValidator::dataStoreId('staff'),
|
||||||
|
'error' => ERRORS::INVALID_STAFF
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handler() {
|
||||||
|
$staffId = Controller::request('staffId');
|
||||||
|
$staff = Staff::getDataStore($staffId);
|
||||||
|
|
||||||
|
foreach($staff->sharedTicketList as $ticket) {
|
||||||
|
$ticket->owner = null;
|
||||||
|
$ticket->true = true;
|
||||||
|
$ticket->store();
|
||||||
|
}
|
||||||
|
|
||||||
|
$staff->delete();
|
||||||
|
Response::respondSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
|
||||||
|
class EditStaffController extends Controller {
|
||||||
|
const PATH = '/edit';
|
||||||
|
|
||||||
|
private $staffRow;
|
||||||
|
private $staffId;
|
||||||
|
|
||||||
|
public function validations() {
|
||||||
|
return [
|
||||||
|
'permission' => 'staff_1',
|
||||||
|
'requestData' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handler() {
|
||||||
|
$this->staffId = Controller::request('staffId');
|
||||||
|
|
||||||
|
if(!$this->staffId) {
|
||||||
|
$this->staffRow = Controller::getLoggedUser();
|
||||||
|
} else if(Controller::isStaffLogged(3)) {
|
||||||
|
$this->staffRow = Staff::getDataStore($this->staffId, 'id');
|
||||||
|
|
||||||
|
if($this->staffRow->isNull()) {
|
||||||
|
Response::respondError(ERRORS::INVALID_STAFF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Response::respondError(ERRORS::NO_PERMISSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->editInformation();
|
||||||
|
Response::respondSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function editInformation() {
|
||||||
|
|
||||||
|
if(Controller::request('email')) {
|
||||||
|
$this->staffRow->email = Controller::request('email');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Controller::request('password')) {
|
||||||
|
$this->staffRow->password = Hashing::hashPassword(Controller::request('password'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Controller::request('level') && Controller::isStaffLogged(3)) {
|
||||||
|
$this->staffRow->level = Controller::request('level');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Controller::request('departments') && Controller::isStaffLogged(3)) {
|
||||||
|
$this->staffRow->sharedDepartmentList = $this->getDepartmentList();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->staffRow->store();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getDepartmentList() {
|
||||||
|
$listDepartments = new DataStoreList();
|
||||||
|
$departmentIds = json_decode(Controller::request('departments'));
|
||||||
|
|
||||||
|
foreach($departmentIds as $id) {
|
||||||
|
$department = Department::getDataStore($id);
|
||||||
|
$listDepartments->add($department);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $listDepartments;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
|
||||||
|
|
||||||
|
class GetAllStaffController extends Controller {
|
||||||
|
const PATH ='/get-all';
|
||||||
|
|
||||||
|
public function validations() {
|
||||||
|
return [
|
||||||
|
'permission' => 'staff_3',
|
||||||
|
'requestData' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function handler() {
|
||||||
|
$staffs = Staff::getAll();
|
||||||
|
$staffArray = [];
|
||||||
|
|
||||||
|
foreach($staffs as $staff) {
|
||||||
|
$assignedTickets = 0;
|
||||||
|
$closedTickets = 0;
|
||||||
|
|
||||||
|
foreach ($staff->sharedTicketList as $ticket) {
|
||||||
|
if($ticket->closed) $closedTickets++;
|
||||||
|
else $assignedTickets++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$staffArray[] = [
|
||||||
|
'id' => $staff->id,
|
||||||
|
'name' => $staff->name,
|
||||||
|
'email' => $staff->email,
|
||||||
|
'profilePic' => $staff->profilePic,
|
||||||
|
'level' => $staff->level,
|
||||||
|
'departments' => $staff->sharedDepartmentList->toArray(),
|
||||||
|
'assignedTickets' => $assignedTickets,
|
||||||
|
'closedTickets' => $closedTickets,
|
||||||
|
'lastLogin' => $staff->lastLogin
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::respondSuccess($staffArray);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,14 @@ class GetStaffController extends Controller {
|
||||||
|
|
||||||
public function handler() {
|
public function handler() {
|
||||||
$user = Controller::getLoggedUser();
|
$user = Controller::getLoggedUser();
|
||||||
|
|
||||||
|
$userId = Controller::request('staffId');
|
||||||
|
$userRow = Staff::getDataStore($userId);
|
||||||
|
|
||||||
|
if($user->level == 3 && !$userRow->isNull()) {
|
||||||
|
$user = $userRow;
|
||||||
|
}
|
||||||
|
|
||||||
$parsedDepartmentList = [];
|
$parsedDepartmentList = [];
|
||||||
$departmentList = $user->sharedDepartmentList;
|
$departmentList = $user->sharedDepartmentList;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ class LoginController extends Controller {
|
||||||
if ($this->checkInputCredentials() || $this->checkRememberToken()) {
|
if ($this->checkInputCredentials() || $this->checkRememberToken()) {
|
||||||
$this->createUserSession();
|
$this->createUserSession();
|
||||||
$this->createSessionCookie();
|
$this->createSessionCookie();
|
||||||
|
if(Controller::request('staff')) {
|
||||||
|
$this->userInstance->lastLogin = Date::getCurrentDate();
|
||||||
|
$this->userInstance->store();
|
||||||
|
}
|
||||||
|
|
||||||
Response::respondSuccess($this->getUserData());
|
Response::respondSuccess($this->getUserData());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,4 +26,7 @@ class ERRORS {
|
||||||
const INVALID_ORDER = 'INVALID_ORDER';
|
const INVALID_ORDER = 'INVALID_ORDER';
|
||||||
const INVALID_USER = 'INVALID_USER';
|
const INVALID_USER = 'INVALID_USER';
|
||||||
const ALREADY_BANNED = 'ALREADY_BANNED';
|
const ALREADY_BANNED = 'ALREADY_BANNED';
|
||||||
|
const INVALID_LEVEL = 'INVALID_LEVEL';
|
||||||
|
const ALREADY_A_STAFF = 'ALREADY_A_STAFF';
|
||||||
|
const INVALID_STAFF = 'INVALID_STAFF';
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@ class DataStoreId extends AbstractRule {
|
||||||
case 'user':
|
case 'user':
|
||||||
$dataStore = \User::getUser($dataStoreId);
|
$dataStore = \User::getUser($dataStoreId);
|
||||||
break;
|
break;
|
||||||
|
case 'staff':
|
||||||
|
$dataStore = \Staff::getUser($dataStoreId);
|
||||||
|
break;
|
||||||
case 'ticket':
|
case 'ticket':
|
||||||
$dataStore = \Ticket::getTicket($dataStoreId);
|
$dataStore = \Ticket::getTicket($dataStoreId);
|
||||||
break;
|
break;
|
||||||
|
@ -45,6 +48,7 @@ class DataStoreId extends AbstractRule {
|
||||||
private function isDataStoreNameValid($dataStoreName) {
|
private function isDataStoreNameValid($dataStoreName) {
|
||||||
return in_array($dataStoreName, [
|
return in_array($dataStoreName, [
|
||||||
'user',
|
'user',
|
||||||
|
'staff',
|
||||||
'ticket',
|
'ticket',
|
||||||
'department',
|
'department',
|
||||||
'customresponse',
|
'customresponse',
|
||||||
|
|
|
@ -24,4 +24,10 @@ class Department extends DataStore {
|
||||||
|
|
||||||
return $departmentsNameList;
|
return $departmentsNameList;
|
||||||
}
|
}
|
||||||
|
public function toArray() {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'name' => $this->name
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,7 +17,8 @@ class Staff extends DataStore {
|
||||||
'profilePic',
|
'profilePic',
|
||||||
'level',
|
'level',
|
||||||
'sharedDepartmentList',
|
'sharedDepartmentList',
|
||||||
'sharedTicketList'
|
'sharedTicketList',
|
||||||
|
'lastLogin'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,4 +31,17 @@ class Staff extends DataStore {
|
||||||
public static function getUser($value, $property = 'id') {
|
public static function getUser($value, $property = 'id') {
|
||||||
return parent::getDataStore($value, $property);
|
return parent::getDataStore($value, $property);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function toArray() {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'email' => $this->email,
|
||||||
|
'profilePic' => $this->profilePic,
|
||||||
|
'level' => $this->level,
|
||||||
|
'departments' => $this->sharedDepartmentList->toArray(),
|
||||||
|
'tickets' => $this->sharedTicketList->toArray(),
|
||||||
|
'lastLogin' => $this->lastLogin
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,10 @@ require './ticket/custom-response.rb'
|
||||||
require './ticket/change-department.rb'
|
require './ticket/change-department.rb'
|
||||||
require './ticket/close.rb'
|
require './ticket/close.rb'
|
||||||
require './ticket/re-open.rb'
|
require './ticket/re-open.rb'
|
||||||
|
require './staff/add.rb'
|
||||||
require './staff/get.rb'
|
require './staff/get.rb'
|
||||||
|
require './staff/edit.rb'
|
||||||
|
require './staff/delete.rb'
|
||||||
require './staff/assign-ticket.rb'
|
require './staff/assign-ticket.rb'
|
||||||
require './staff/un-assign-ticket.rb'
|
require './staff/un-assign-ticket.rb'
|
||||||
require './staff/get-tickets.rb'
|
require './staff/get-tickets.rb'
|
||||||
|
@ -39,5 +42,6 @@ require './user/get-user.rb'
|
||||||
require './user/ban.rb'
|
require './user/ban.rb'
|
||||||
require './user/get-users-test.rb'
|
require './user/get-users-test.rb'
|
||||||
require './user/delete.rb'
|
require './user/delete.rb'
|
||||||
|
require './staff/get-all.rb'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
describe'/staff/add' do
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
|
it 'should add staff member' do
|
||||||
|
result= request('/staff/add', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
name: 'Tyrion Lannister',
|
||||||
|
email: 'tyrion@opensupports.com',
|
||||||
|
password: 'testpassword',
|
||||||
|
level: 2,
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
departments: '[1]'
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
|
row = $database.getRow('staff', result['data']['id'], 'id')
|
||||||
|
|
||||||
|
(row['name']).should.equal('Tyrion Lannister')
|
||||||
|
(row['email']).should.equal('tyrion@opensupports.com')
|
||||||
|
(row['profile_pic']).should.equal('http://www.opensupports.com/profilepic.jpg')
|
||||||
|
(row['level']).should.equal('2')
|
||||||
|
end
|
||||||
|
it 'should fail if staff member is alrady a staff' do
|
||||||
|
result= request('/staff/add', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
name: 'Tyrion Lannister',
|
||||||
|
email: 'tyrion@opensupports.com',
|
||||||
|
password: 'testpassword',
|
||||||
|
level: 2,
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
departments: '[1]'
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('fail')
|
||||||
|
(result['message']).should.equal('ALREADY_A_STAFF')
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
describe'/staff/delete' do
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
|
it 'should delete staff member' do
|
||||||
|
result= request('/staff/delete', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
staffId:2
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
|
row = $database.getRow('staff', 2, 'id')
|
||||||
|
(row).should.equal(nil)
|
||||||
|
|
||||||
|
end
|
||||||
|
it 'should fail delete if staff member is does not exist' do
|
||||||
|
result= request('/staff/delete', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
staffId:2
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('fail')
|
||||||
|
(result['message']).should.equal('INVALID_STAFF')
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,56 @@
|
||||||
|
describe'/staff/edit' do
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
|
it 'should edit another staff member' do
|
||||||
|
result= request('/staff/edit', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
email: 'LittleLannister@opensupports.com',
|
||||||
|
level: 1,
|
||||||
|
departments: '[1, 2]',
|
||||||
|
staffId: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
|
row = $database.getRow('staff', 2, 'id')
|
||||||
|
|
||||||
|
(row['email']).should.equal('LittleLannister@opensupports.com')
|
||||||
|
(row['level']).should.equal('1')
|
||||||
|
|
||||||
|
rows = $database.getRow('department_staff', 2, 'staff_id')
|
||||||
|
|
||||||
|
(rows['department_id']).should.equal('1')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should edit staff member ' do
|
||||||
|
request('/staff/add', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
name: 'Arya Stark',
|
||||||
|
password: 'starkpassword',
|
||||||
|
email: 'arya@opensupports.com',
|
||||||
|
level: 2,
|
||||||
|
profilePic: 'http://www.opensupports.com/profilepic.jpg',
|
||||||
|
departments: '[1]'
|
||||||
|
})
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login('arya@opensupports.com', 'starkpassword', true)
|
||||||
|
|
||||||
|
result = request('/staff/edit', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
email: 'newwstaff@opensupports.com',
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
|
row = $database.getRow('staff', $csrf_userid, 'id')
|
||||||
|
|
||||||
|
(row['email']).should.equal('newwstaff@opensupports.com')
|
||||||
|
(row['level']).should.equal('2')
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,35 @@
|
||||||
|
describe'/staff/get-all' do
|
||||||
|
request('/user/logout')
|
||||||
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
|
it 'should get all staff member' do
|
||||||
|
result= request('/staff/get-all', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
|
(result['data'][0]['name']).should.equal('Emilia Clarke')
|
||||||
|
(result['data'][0]['email']).should.equal('staff@opensupports.com')
|
||||||
|
(result['data'][0]['profilePic']).should.equal('http://www.opensupports.com/profilepic.jpg')
|
||||||
|
(result['data'][0]['level']).should.equal('3')
|
||||||
|
(result['data'][0]['departments'][0]['id']).should.equal('1')
|
||||||
|
(result['data'][0]['departments'][0]['name']).should.equal('Tech Support')
|
||||||
|
(result['data'][0]['departments'][1]['id']).should.equal('2')
|
||||||
|
(result['data'][0]['departments'][1]['name']).should.equal('Suggestions')
|
||||||
|
(result['data'][0]['departments'][2]['id']).should.equal('3')
|
||||||
|
(result['data'][0]['departments'][2]['name']).should.equal('Sales and Subscriptions')
|
||||||
|
(result['data'][0]['assignedTickets']).should.equal(3)
|
||||||
|
(result['data'][0]['closedTickets']).should.equal(0)
|
||||||
|
|
||||||
|
(result['data'][1]['name']).should.equal('Arya Stark')
|
||||||
|
(result['data'][1]['email']).should.equal('newwstaff@opensupports.com')
|
||||||
|
(result['data'][1]['profilePic']).should.equal('http://www.opensupports.com/profilepic.jpg')
|
||||||
|
(result['data'][1]['level']).should.equal('2')
|
||||||
|
(result['data'][1]['departments'][0]['id']).should.equal('1')
|
||||||
|
(result['data'][1]['departments'][0]['name']).should.equal('Tech Support')
|
||||||
|
(result['data'][1]['assignedTickets']).should.equal(0)
|
||||||
|
(result['data'][1]['closedTickets']).should.equal(0)
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,5 +11,20 @@ describe '/staff/get/' do
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
(result['data']['name']).should.equal('Emilia Clarke')
|
(result['data']['name']).should.equal('Emilia Clarke')
|
||||||
(result['data']['staff']).should.equal(true)
|
(result['data']['staff']).should.equal(true)
|
||||||
|
(result['data']['email']).should.equal('staff@opensupports.com')
|
||||||
|
(result['data']['level']).should.equal('3')
|
||||||
|
end
|
||||||
|
it 'should return staff member data with staff Id' do
|
||||||
|
result = request('/staff/get', {
|
||||||
|
csrf_userid: $csrf_userid,
|
||||||
|
csrf_token: $csrf_token,
|
||||||
|
staffId:2
|
||||||
|
})
|
||||||
|
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
(result['data']['name']).should.equal('Tyrion Lannister')
|
||||||
|
(result['data']['staff']).should.equal(true)
|
||||||
|
(result['data']['email']).should.equal('tyrion@opensupports.com')
|
||||||
|
(result['data']['level']).should.equal('2')
|
||||||
end
|
end
|
||||||
end
|
end
|
Loading…
Reference in New Issue