Ivan - Fix AreYouSure design, fix stats, rename get-api-keys, use allow-attachment, fix login verificationToken, fix deletion, fix ticket view permission, fix last events when empty, fix configuration in frontend, fix system preferences and my account
This commit is contained in:
parent
3d27415041
commit
5126b40538
|
@ -1,9 +1,9 @@
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
openModal(content) {
|
openModal(config) {
|
||||||
return {
|
return {
|
||||||
type: 'OPEN_MODAL',
|
type: 'OPEN_MODAL',
|
||||||
payload: content
|
payload: config
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class ActivityRow extends React.Component {
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
<span className="activity-row__message"> {i18n('ACTIVITY_' + this.props.type)} </span>
|
<span className="activity-row__message"> {i18n('ACTIVITY_' + this.props.type)} </span>
|
||||||
{_.includes(ticketRelatedTypes, this.props.type) ? this.renderTicketNumber() : null}
|
{_.includes(ticketRelatedTypes, this.props.type) ? this.renderTicketNumber() : this.props.to}
|
||||||
<span className="separator" />
|
<span className="separator" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
&__ticket-link {
|
&__ticket-link {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
import ModalContainer from 'app-components/modal-container';
|
||||||
|
|
||||||
import Button from 'core-components/button';
|
import Button from 'core-components/button';
|
||||||
import Input from 'core-components/input';
|
import Input from 'core-components/input';
|
||||||
import ModalContainer from 'app-components/modal-container';
|
import Icon from 'core-components/icon';
|
||||||
|
|
||||||
|
|
||||||
class AreYouSure extends React.Component {
|
class AreYouSure extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -26,7 +29,8 @@ class AreYouSure extends React.Component {
|
||||||
|
|
||||||
static openModal(description, onYes, type) {
|
static openModal(description, onYes, type) {
|
||||||
ModalContainer.openModal(
|
ModalContainer.openModal(
|
||||||
<AreYouSure description={description} onYes={onYes} type={type}/>
|
<AreYouSure description={description} onYes={onYes} type={type}/>,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,21 +44,25 @@ class AreYouSure extends React.Component {
|
||||||
<div className="are-you-sure__header" id="are-you-sure__header">
|
<div className="are-you-sure__header" id="are-you-sure__header">
|
||||||
{i18n('ARE_YOU_SURE')}
|
{i18n('ARE_YOU_SURE')}
|
||||||
</div>
|
</div>
|
||||||
|
<span className="are-you-sure__close-icon" onClick={this.onNo.bind(this)}>
|
||||||
|
<Icon name="times" size="2x"/>
|
||||||
|
</span>
|
||||||
<div className="are-you-sure__description" id="are-you-sure__description">
|
<div className="are-you-sure__description" id="are-you-sure__description">
|
||||||
{this.props.description || (this.props.type === 'secure' && i18n('PLEASE_CONFIRM_PASSWORD'))}
|
{this.props.description || (this.props.type === 'secure' && i18n('PLEASE_CONFIRM_PASSWORD'))}
|
||||||
</div>
|
</div>
|
||||||
{(this.props.type === 'secure') ? this.renderPassword() : null}
|
{(this.props.type === 'secure') ? this.renderPassword() : null}
|
||||||
|
<span className="separator" />
|
||||||
<div className="are-you-sure__buttons">
|
<div className="are-you-sure__buttons">
|
||||||
<div className="are-you-sure__yes-button">
|
|
||||||
<Button type="secondary" size="small" onClick={this.onYes.bind(this)} ref="yesButton" tabIndex="2">
|
|
||||||
{i18n('YES')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="are-you-sure__no-button">
|
<div className="are-you-sure__no-button">
|
||||||
<Button type="link" size="auto" onClick={this.onNo.bind(this)} tabIndex="2">
|
<Button type="link" size="auto" onClick={this.onNo.bind(this)} tabIndex="2">
|
||||||
{i18n('CANCEL')}
|
{i18n('CANCEL')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="are-you-sure__yes-button">
|
||||||
|
<Button type="secondary" size="small" onClick={this.onYes.bind(this)} ref="yesButton" tabIndex="2">
|
||||||
|
{i18n('YES')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -62,7 +70,7 @@ class AreYouSure extends React.Component {
|
||||||
|
|
||||||
renderPassword() {
|
renderPassword() {
|
||||||
return (
|
return (
|
||||||
<Input className="are-you-sure__password" password placeholder="password" name="password" value={this.state.password} onChange={this.onPasswordChange.bind(this)} onKeyDown={this.onInputKeyDown.bind(this)}/>
|
<Input className="are-you-sure__password" password placeholder="password" name="password" ref="password" size="medium" value={this.state.password} onChange={this.onPasswordChange.bind(this)} onKeyDown={this.onInputKeyDown.bind(this)}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +87,11 @@ class AreYouSure extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onYes() {
|
onYes() {
|
||||||
if (this.props.type === 'default' || this.state.password){
|
if (this.props.type === 'secure' && !this.state.password) {
|
||||||
|
this.refs.password.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.type === 'default' || this.state.password) {
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
|
||||||
if (this.props.onYes) {
|
if (this.props.onYes) {
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
@import "../scss/vars";
|
@import "../scss/vars";
|
||||||
|
|
||||||
.are-you-sure {
|
.are-you-sure {
|
||||||
width: 400px;
|
width: 800px;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
color: $primary-red;
|
background-color: $secondary-blue;
|
||||||
font-size: $font-size--xl;
|
border-top-right-radius: 4px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
color: white;
|
||||||
|
font-size: $font-size--lg;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 10px;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
&__description {
|
||||||
color: $dark-grey;
|
color: $dark-grey;
|
||||||
font-size: $font-size--md;
|
font-size: $font-size--md;
|
||||||
margin-bottom: 50px;
|
padding: 14px 5% 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
margin: 0 auto;
|
margin-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__yes-button,
|
&__yes-button,
|
||||||
|
@ -30,7 +35,25 @@
|
||||||
|
|
||||||
&__password {
|
&__password {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin-top: -30px;
|
margin-top: 20px;
|
||||||
margin-bottom: 20px;
|
}
|
||||||
|
|
||||||
|
&__close-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
width: 90%;
|
||||||
|
margin: 30px auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
.are-you-sure {
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import keyCode from 'keycode';
|
import keyCode from 'keycode';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import store from 'app/store';
|
import store from 'app/store';
|
||||||
import ModalActions from 'actions/modal-actions';
|
import ModalActions from 'actions/modal-actions';
|
||||||
|
@ -8,11 +9,12 @@ import Modal from 'core-components/modal';
|
||||||
|
|
||||||
class ModalContainer extends React.Component {
|
class ModalContainer extends React.Component {
|
||||||
|
|
||||||
static openModal(content) {
|
static openModal(content, noPadding) {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
ModalActions.openModal(
|
ModalActions.openModal({
|
||||||
content
|
content,
|
||||||
)
|
noPadding
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ class ModalContainer extends React.Component {
|
||||||
|
|
||||||
renderModal() {
|
renderModal() {
|
||||||
return (
|
return (
|
||||||
<Modal content={this.props.modal.content} />
|
<Modal content={this.props.modal.content} noPadding={this.props.modal.noPadding}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ class PeopleList extends React.Component {
|
||||||
name: React.PropTypes.node,
|
name: React.PropTypes.node,
|
||||||
assignedTickets: React.PropTypes.number,
|
assignedTickets: React.PropTypes.number,
|
||||||
closedTickets: React.PropTypes.number,
|
closedTickets: React.PropTypes.number,
|
||||||
lastLogin: React.PropTypes.number
|
lastLogin: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.string])
|
||||||
})),
|
})),
|
||||||
pageSize: React.PropTypes.number,
|
pageSize: React.PropTypes.number,
|
||||||
page: React.PropTypes.number,
|
page: React.PropTypes.number,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import RichTextEditor from 'react-rte-browserify';
|
import RichTextEditor from 'react-rte-browserify';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
|
@ -190,7 +191,7 @@ class TicketViewer extends React.Component {
|
||||||
<div className="ticket-viewer__response-field row">
|
<div className="ticket-viewer__response-field row">
|
||||||
<Form {...this.getCommentFormProps()}>
|
<Form {...this.getCommentFormProps()}>
|
||||||
<FormField name="content" validation="TEXT_AREA" required field="textarea" />
|
<FormField name="content" validation="TEXT_AREA" required field="textarea" />
|
||||||
<FormField name="file" field="file"/>
|
{(this.props.allowAttachments) ? <FormField name="file" field="file"/> : null}
|
||||||
<SubmitButton>{i18n('RESPOND_TICKET')}</SubmitButton>
|
<SubmitButton>{i18n('RESPOND_TICKET')}</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -333,7 +334,9 @@ class TicketViewer extends React.Component {
|
||||||
onCommentSuccess() {
|
onCommentSuccess() {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
commentError: false
|
commentValue: RichTextEditor.createEmptyValue(),
|
||||||
|
commentError: false,
|
||||||
|
commentEdited: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.onTicketModification();
|
this.onTicketModification();
|
||||||
|
@ -353,4 +356,8 @@ class TicketViewer extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TicketViewer;
|
export default connect((store) => {
|
||||||
|
return {
|
||||||
|
allowAttachments: store.config['allow-attachments']
|
||||||
|
};
|
||||||
|
})(TicketViewer);
|
||||||
|
|
|
@ -23,11 +23,11 @@ class AdminPanelMyAccount extends React.Component {
|
||||||
getEditorProps() {
|
getEditorProps() {
|
||||||
return {
|
return {
|
||||||
myAccount: true,
|
myAccount: true,
|
||||||
staffId: this.props.userId,
|
staffId: this.props.userId * 1,
|
||||||
name: this.props.userName,
|
name: this.props.userName,
|
||||||
email: this.props.userEmail,
|
email: this.props.userEmail,
|
||||||
profilePic: this.props.userProfilePic,
|
profilePic: this.props.userProfilePic,
|
||||||
level: this.props.userLevel,
|
level: this.props.userLevel * 1,
|
||||||
departments: this.props.userDepartments,
|
departments: this.props.userDepartments,
|
||||||
onChange: () => this.props.dispatch(SessionActions.getUserData(null, null, true))
|
onChange: () => this.props.dispatch(SessionActions.getUserData(null, null, true))
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ class AdminPanelStats extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="admin-panel-stats">
|
<div className="admin-panel-stats">
|
||||||
<Header title={i18n('STATISTICS')} description={i18n('STATISTICS_DESCRIPTION')}/>
|
<Header title={i18n('STATISTICS')} description={i18n('STATISTICS_DESCRIPTION')}/>
|
||||||
<Stats type="general"/>
|
<Stats type="general"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -213,15 +213,15 @@ class AdminPanelSystemPreferences extends React.Component {
|
||||||
'reCaptchaPrivate': result.data.reCaptchaPrivate,
|
'reCaptchaPrivate': result.data.reCaptchaPrivate,
|
||||||
'url': result.data['url'],
|
'url': result.data['url'],
|
||||||
'title': result.data['title'],
|
'title': result.data['title'],
|
||||||
'layout': result.data['layout'] == 'full-width' ? 1 : 0,
|
'layout': (result.data['layout'] == 'full-width') ? 1 : 0,
|
||||||
'time-zone': result.data['time-zone'],
|
'time-zone': result.data['time-zone'],
|
||||||
'no-reply-email': result.data['no-reply-email'],
|
'no-reply-email': result.data['no-reply-email'],
|
||||||
'smtp-host': result.data['smtp-host'],
|
'smtp-host': result.data['smtp-host'],
|
||||||
'smtp-port': result.data['smtp-port'],
|
'smtp-port': result.data['smtp-port'],
|
||||||
'smtp-user': result.data['smtp-user'],
|
'smtp-user': result.data['smtp-user'],
|
||||||
'smtp-pass': '',
|
'smtp-pass': '',
|
||||||
'maintenance-mode': result.data['maintenance-mode'],
|
'maintenance-mode': !!(result.data['maintenance-mode'] * 1),
|
||||||
'allow-attachments': result.data['allow-attachments'],
|
'allow-attachments': !!(result.data['allow-attachments'] * 1),
|
||||||
'max-size': result.data['max-size'],
|
'max-size': result.data['max-size'],
|
||||||
'allowedLanguages': result.data.allowedLanguages.map(lang => (_.indexOf(languageKeys, lang))),
|
'allowedLanguages': result.data.allowedLanguages.map(lang => (_.indexOf(languageKeys, lang))),
|
||||||
'supportedLanguages': result.data.supportedLanguages.map(lang => (_.indexOf(languageKeys, lang)))
|
'supportedLanguages': result.data.supportedLanguages.map(lang => (_.indexOf(languageKeys, lang)))
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
&__update-name-button {
|
&__update-name-button {
|
||||||
float: left;
|
float: left;
|
||||||
|
min-width: 156px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__optional-buttons {
|
&__optional-buttons {
|
||||||
|
|
|
@ -68,9 +68,9 @@ class AdminPanelStaffMembers extends React.Component {
|
||||||
if(!this.state.selectedDepartment) {
|
if(!this.state.selectedDepartment) {
|
||||||
staffList = this.state.staffList;
|
staffList = this.state.staffList;
|
||||||
} else {
|
} else {
|
||||||
staffList = _.filter(this.state.staffList, (o) => {
|
staffList = _.filter(this.state.staffList, (staff) => {
|
||||||
return _.findIndex(o.departments, {id: this.state.selectedDepartment}) !== -1;
|
return _.findIndex(staff.departments, {id: this.state.selectedDepartment}) !== -1;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return staffList.map(staff => {
|
return staffList.map(staff => {
|
||||||
|
|
|
@ -31,6 +31,10 @@ class StaffEditor extends React.Component {
|
||||||
onDelete: React.PropTypes.func
|
onDelete: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
tickets: []
|
||||||
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
email: this.props.email,
|
email: this.props.email,
|
||||||
level: this.props.level - 1,
|
level: this.props.level - 1,
|
||||||
|
|
|
@ -151,8 +151,8 @@ class AdminPanelListUsers extends React.Component {
|
||||||
|
|
||||||
onUsersRetrieved(result) {
|
onUsersRetrieved(result) {
|
||||||
this.setState({
|
this.setState({
|
||||||
page: result.data.page,
|
page: result.data.page * 1,
|
||||||
pages: result.data.pages,
|
pages: result.data.pages * 1,
|
||||||
users: result.data.users,
|
users: result.data.users,
|
||||||
orderBy: result.data.orderBy,
|
orderBy: result.data.orderBy,
|
||||||
desc: (result.data.desc === '1'),
|
desc: (result.data.desc === '1'),
|
||||||
|
|
|
@ -153,9 +153,7 @@ let DemoPage = React.createClass({
|
||||||
title: 'ModalTrigger',
|
title: 'ModalTrigger',
|
||||||
render: (
|
render: (
|
||||||
<Button onClick={function () {
|
<Button onClick={function () {
|
||||||
ModalContainer.openModal(
|
AreYouSure.openModal('I confirm I want to perform this action.', ()=> {alert('yes');}, 'secure')
|
||||||
<AreYouSure description="I confirm I want to perform this action." onYes={()=> {alert('yes');}} />
|
|
||||||
);
|
|
||||||
}}>
|
}}>
|
||||||
Open Modal
|
Open Modal
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import React from 'react';
|
||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
|
||||||
import ModalContainer from 'app-components/modal-container';
|
|
||||||
import AreYouSure from 'app-components/are-you-sure';
|
import AreYouSure from 'app-components/are-you-sure';
|
||||||
|
|
||||||
import Header from 'core-components/header';
|
import Header from 'core-components/header';
|
||||||
|
@ -64,11 +63,11 @@ class DashboardEditProfilePage extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSubmitEditEmail(formState) {
|
onSubmitEditEmail(formState) {
|
||||||
ModalContainer.openModal(<AreYouSure onYes={this.callEditEmailAPI.bind(this, formState)}/>);
|
AreYouSure.openModal(i18n('EMAIL_WILL_CHANGE'), this.callEditEmailAPI.bind(this, formState));
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmitEditPassword(formState) {
|
onSubmitEditPassword(formState) {
|
||||||
ModalContainer.openModal(<AreYouSure onYes={this.callEditPassAPI.bind(this, formState)}/>);
|
AreYouSure.openModal(i18n('PASSWORD_WILL_CHANGE'), this.callEditPassAPI.bind(this, formState));
|
||||||
}
|
}
|
||||||
|
|
||||||
callEditEmailAPI(formState){
|
callEditEmailAPI(formState){
|
||||||
|
|
|
@ -32,7 +32,7 @@ class MainLayoutHeader extends React.Component {
|
||||||
result = (
|
result = (
|
||||||
<div className="main-layout-header__login-links">
|
<div className="main-layout-header__login-links">
|
||||||
<Button type="clean" route={{to:'/'}}>{i18n('LOG_IN')}</Button>
|
<Button type="clean" route={{to:'/'}}>{i18n('LOG_IN')}</Button>
|
||||||
{(this.props.config['registration']) ? <Button type="clean" route={{to:'/signup'}}>{i18n('SIGN_UP')}</Button> : null}
|
{(!!(this.props.config['registration'] * 1)) ? <Button type="clean" route={{to:'/signup'}}>{i18n('SIGN_UP')}</Button> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import {Motion, spring} from 'react-motion';
|
import {Motion, spring} from 'react-motion';
|
||||||
|
|
||||||
class Modal extends React.Component {
|
class Modal extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
content: React.PropTypes.node
|
content: React.PropTypes.node,
|
||||||
|
noPadding: React.PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -30,13 +32,22 @@ class Modal extends React.Component {
|
||||||
|
|
||||||
renderModal(animation) {
|
renderModal(animation) {
|
||||||
return (
|
return (
|
||||||
<div className="modal" style={{opacity: animation.fade}}>
|
<div className={this.getClass()} style={{opacity: animation.fade}}>
|
||||||
<div className="modal__content" style={{transform: 'scale(' + animation.scale + ')'}}>
|
<div className="modal__content" style={{transform: 'scale(' + animation.scale + ')'}}>
|
||||||
{this.props.content}
|
{this.props.content}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getClass() {
|
||||||
|
let classes = {
|
||||||
|
'modal': true,
|
||||||
|
'modal_no-padding': this.props.noPadding
|
||||||
|
};
|
||||||
|
|
||||||
|
return classNames(classes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Modal;
|
export default Modal;
|
|
@ -18,4 +18,11 @@
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
box-shadow: 0 0 10px white;
|
box-shadow: 0 0 10px white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&_no-padding {
|
||||||
|
|
||||||
|
.modal__content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -281,7 +281,9 @@ export default {
|
||||||
'TICKET_SENT': 'Ticket has been created successfully.',
|
'TICKET_SENT': 'Ticket has been created successfully.',
|
||||||
'VALID_RECOVER': 'Password recovered successfully',
|
'VALID_RECOVER': 'Password recovered successfully',
|
||||||
'EMAIL_EXISTS': 'Email already exists',
|
'EMAIL_EXISTS': 'Email already exists',
|
||||||
'ARE_YOU_SURE': 'Are you sure?',
|
'ARE_YOU_SURE': 'Confirm action',
|
||||||
|
'EMAIL_WILL_CHANGE': 'The current email will be changed',
|
||||||
|
'PASSWORD_WILL_CHANGE': 'The current password will be changed',
|
||||||
'EMAIL_CHANGED': 'Email has been changed successfully',
|
'EMAIL_CHANGED': 'Email has been changed successfully',
|
||||||
'PASSWORD_CHANGED': 'Password has been changed successfully',
|
'PASSWORD_CHANGED': 'Password has been changed successfully',
|
||||||
'OLD_PASSWORD_INCORRECT': 'Old password is incorrect',
|
'OLD_PASSWORD_INCORRECT': 'Old password is incorrect',
|
||||||
|
|
|
@ -29,6 +29,7 @@ function processData (data, dataAsForm = false) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
call: function ({path, data, plain, dataAsForm}) {
|
call: function ({path, data, plain, dataAsForm}) {
|
||||||
|
console.log('request ' + path, data);
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
APIUtils.post(apiUrl + path, processData(data, dataAsForm), dataAsForm)
|
APIUtils.post(apiUrl + path, processData(data, dataAsForm), dataAsForm)
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
|
|
|
@ -62,6 +62,7 @@ class SessionStore {
|
||||||
this.setItem('title', configs.title);
|
this.setItem('title', configs.title);
|
||||||
this.setItem('registration', configs.registration);
|
this.setItem('registration', configs.registration);
|
||||||
this.setItem('user-system-enabled', configs['user-system-enabled']);
|
this.setItem('user-system-enabled', configs['user-system-enabled']);
|
||||||
|
this.setItem('allow-attachments', configs['allow-attachments']);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigs() {
|
getConfigs() {
|
||||||
|
@ -72,9 +73,11 @@ class SessionStore {
|
||||||
allowedLanguages: JSON.parse(this.getItem('allowedLanguages')),
|
allowedLanguages: JSON.parse(this.getItem('allowedLanguages')),
|
||||||
supportedLanguages: JSON.parse(this.getItem('supportedLanguages')),
|
supportedLanguages: JSON.parse(this.getItem('supportedLanguages')),
|
||||||
layout: this.getItem('layout'),
|
layout: this.getItem('layout'),
|
||||||
registration: this.getItem('registration'),
|
|
||||||
title: this.getItem('title'),
|
title: this.getItem('title'),
|
||||||
['user-system-enabled']: this.getItem('user-system-enabled')
|
registration: !!(this.getItem('registration') * 1),
|
||||||
|
'user-system-enabled': !!(this.getItem('user-system-enabled') * 1),
|
||||||
|
'allow-attachments': !!(this.getItem('allow-attachments') * 1),
|
||||||
|
'maintenance-mode': !!(this.getItem('maintenance-mode') * 1)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ class AdminDataReducer extends Reducer {
|
||||||
getTypeHandlers() {
|
getTypeHandlers() {
|
||||||
return {
|
return {
|
||||||
'CUSTOM_RESPONSES_FULFILLED': this.onCustomResponses,
|
'CUSTOM_RESPONSES_FULFILLED': this.onCustomResponses,
|
||||||
'SESSION_CHECKED': this.onSessionChecked,
|
|
||||||
|
|
||||||
'MY_TICKETS_FULFILLED': this.onMyTicketsRetrieved,
|
'MY_TICKETS_FULFILLED': this.onMyTicketsRetrieved,
|
||||||
'MY_TICKETS_REJECTED': this.onMyTicketsRejected,
|
'MY_TICKETS_REJECTED': this.onMyTicketsRejected,
|
||||||
|
@ -51,15 +50,6 @@ class AdminDataReducer extends Reducer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSessionChecked(state) {
|
|
||||||
const customResponses = sessionStore.getItem('customResponses');
|
|
||||||
|
|
||||||
return _.extend({}, state, {
|
|
||||||
customResponses: JSON.parse(customResponses),
|
|
||||||
customResponsesLoaded: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMyTicketsRetrieved(state, payload) {
|
onMyTicketsRetrieved(state, payload) {
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
myTickets: payload.data,
|
myTickets: payload.data,
|
||||||
|
|
|
@ -37,6 +37,10 @@ class ConfigReducer extends Reducer {
|
||||||
|
|
||||||
return _.extend({}, state, payload.data, {
|
return _.extend({}, state, payload.data, {
|
||||||
language: currentLanguage || payload.language,
|
language: currentLanguage || payload.language,
|
||||||
|
registration: !!(payload.data.registration * 1),
|
||||||
|
'user-system-enabled': !!(payload.data['user-system-enabled']* 1),
|
||||||
|
'allow-attachments': !!(payload.data['allow-attachments']* 1),
|
||||||
|
'maintenance-mode': !!(payload.data['maintenance-mode']* 1),
|
||||||
initDone: true
|
initDone: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ class ModalReducer extends Reducer {
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
opened: false,
|
opened: false,
|
||||||
|
noPadding: false,
|
||||||
content: null
|
content: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -23,7 +24,8 @@ class ModalReducer extends Reducer {
|
||||||
|
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
opened: true,
|
opened: true,
|
||||||
content: payload
|
content: payload.content,
|
||||||
|
noPadding: payload.noPadding || false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +34,8 @@ class ModalReducer extends Reducer {
|
||||||
|
|
||||||
return _.extend({}, state, {
|
return _.extend({}, state, {
|
||||||
opened: false,
|
opened: false,
|
||||||
content: null
|
content: null,
|
||||||
|
noPadding: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,11 @@ class LastEventsStaffController extends Controller {
|
||||||
$query = substr($query,0,-3);
|
$query = substr($query,0,-3);
|
||||||
$query .= ') ORDER BY id desc LIMIT ? OFFSET ?' ;
|
$query .= ') ORDER BY id desc LIMIT ? OFFSET ?' ;
|
||||||
|
|
||||||
$eventList = Ticketevent::find($query, [10, 10*($page-1)]);
|
if(Ticketevent::count() && !$user->sharedTicketList->isEmpty()) {
|
||||||
|
$eventList = Ticketevent::find($query, [10, 10*($page-1)]);
|
||||||
Response::respondSuccess($eventList->toArray());
|
Response::respondSuccess($eventList->toArray());
|
||||||
|
} else {
|
||||||
|
Response::respondSuccess([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ require_once 'system/disable-user-system.php';
|
||||||
require_once 'system/enabled-user-system.php';
|
require_once 'system/enabled-user-system.php';
|
||||||
require_once 'system/add-api-key.php';
|
require_once 'system/add-api-key.php';
|
||||||
require_once 'system/delete-api-key.php';
|
require_once 'system/delete-api-key.php';
|
||||||
require_once 'system/get-all-keys.php';
|
require_once 'system/get-api-keys.php';
|
||||||
require_once 'system/get-stats.php';
|
require_once 'system/get-stats.php';
|
||||||
require_once 'system/delete-all-users.php';
|
require_once 'system/delete-all-users.php';
|
||||||
require_once 'system/csv-import.php';
|
require_once 'system/csv-import.php';
|
||||||
|
@ -40,7 +40,7 @@ $systemControllerGroup->addController(new EnableRegistrationController);
|
||||||
$systemControllerGroup->addController(new GetStatsController);
|
$systemControllerGroup->addController(new GetStatsController);
|
||||||
$systemControllerGroup->addController(new AddAPIKeyController);
|
$systemControllerGroup->addController(new AddAPIKeyController);
|
||||||
$systemControllerGroup->addController(new DeleteAPIKeyController);
|
$systemControllerGroup->addController(new DeleteAPIKeyController);
|
||||||
$systemControllerGroup->addController(new GetAllKeyController);
|
$systemControllerGroup->addController(new GetAPIKeysController);
|
||||||
$systemControllerGroup->addController(new DeleteAllUsersController);
|
$systemControllerGroup->addController(new DeleteAllUsersController);
|
||||||
$systemControllerGroup->addController(new BackupDatabaseController);
|
$systemControllerGroup->addController(new BackupDatabaseController);
|
||||||
$systemControllerGroup->addController(new DownloadController);
|
$systemControllerGroup->addController(new DownloadController);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
use Respect\Validation\Validator as DataValidator;
|
use Respect\Validation\Validator as DataValidator;
|
||||||
|
|
||||||
class GetAllKeyController extends Controller {
|
class GetAPIKeysController extends Controller {
|
||||||
const PATH = '/get-all-keys';
|
const PATH = '/get-api-keys';
|
||||||
const METHOD = 'POST';
|
const METHOD = 'POST';
|
||||||
|
|
||||||
public function validations() {
|
public function validations() {
|
|
@ -86,6 +86,6 @@ class TicketGetController extends Controller {
|
||||||
$user = Controller::getLoggedUser();
|
$user = Controller::getLoggedUser();
|
||||||
|
|
||||||
return (!Controller::isStaffLogged() && (Controller::isUserSystemEnabled() && $this->ticket->author->id !== $user->id)) ||
|
return (!Controller::isStaffLogged() && (Controller::isUserSystemEnabled() && $this->ticket->author->id !== $user->id)) ||
|
||||||
(Controller::isStaffLogged() && $this->ticket->owner && $this->ticket->owner->id !== $user->id);
|
(Controller::isStaffLogged() && !$user->sharedDepartmentList->includesId($this->ticket->department->id));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,6 +30,11 @@ class DeleteUserController extends Controller {
|
||||||
|
|
||||||
Log::createLog('DELETE_USER', $user->name);
|
Log::createLog('DELETE_USER', $user->name);
|
||||||
RedBean::exec('DELETE FROM log WHERE author_user_id = ?', [$userId]);
|
RedBean::exec('DELETE FROM log WHERE author_user_id = ?', [$userId]);
|
||||||
|
|
||||||
|
foreach($user->sharedTicketList as $ticket) {
|
||||||
|
$ticket->delete();
|
||||||
|
}
|
||||||
|
|
||||||
$user->delete();
|
$user->delete();
|
||||||
|
|
||||||
Response::respondSuccess();
|
Response::respondSuccess();
|
||||||
|
|
|
@ -24,6 +24,11 @@ class LoginController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->checkInputCredentials() || $this->checkRememberToken()) {
|
if ($this->checkInputCredentials() || $this->checkRememberToken()) {
|
||||||
|
if($this->userInstance->verificationToken !== null) {
|
||||||
|
Response::respondError(ERRORS::UNVERIFIED_USER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->createUserSession();
|
$this->createUserSession();
|
||||||
$this->createSessionCookie();
|
$this->createSessionCookie();
|
||||||
if(Controller::request('staff')) {
|
if(Controller::request('staff')) {
|
||||||
|
@ -31,14 +36,6 @@ class LoginController extends Controller {
|
||||||
$this->userInstance->store();
|
$this->userInstance->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
$email = Controller::request('email');
|
|
||||||
$userRow = User::getDataStore($email, 'email');
|
|
||||||
|
|
||||||
if($userRow->verificationToken !== null) {
|
|
||||||
Response::respondError(ERRORS::UNVERIFIED_USER);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::respondSuccess($this->getUserData());
|
Response::respondSuccess($this->getUserData());
|
||||||
} else {
|
} else {
|
||||||
Response::respondError(ERRORS::INVALID_CREDENTIALS);
|
Response::respondError(ERRORS::INVALID_CREDENTIALS);
|
||||||
|
|
|
@ -79,7 +79,9 @@ abstract class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uploadFile() {
|
public function uploadFile() {
|
||||||
if(!isset($_FILES['file'])) return '';
|
$allowAttachments = Setting::getSetting('allow-attachments')->getValue();
|
||||||
|
|
||||||
|
if(!isset($_FILES['file']) || !$allowAttachments) return '';
|
||||||
|
|
||||||
$maxSize = Setting::getSetting('max-size')->getValue();
|
$maxSize = Setting::getSetting('max-size')->getValue();
|
||||||
$fileGap = Setting::getSetting('file-gap')->getValue();
|
$fileGap = Setting::getSetting('file-gap')->getValue();
|
||||||
|
|
|
@ -45,6 +45,10 @@ class DataStoreList implements IteratorAggregate {
|
||||||
return $includes;
|
return $includes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isEmpty() {
|
||||||
|
return empty($list);
|
||||||
|
}
|
||||||
|
|
||||||
public function toBeanList() {
|
public function toBeanList() {
|
||||||
$beanList = [];
|
$beanList = [];
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ require './system/disable-registration.rb'
|
||||||
require './system/enable-registration.rb'
|
require './system/enable-registration.rb'
|
||||||
require './system/add-api-key.rb'
|
require './system/add-api-key.rb'
|
||||||
require './system/delete-api-key.rb'
|
require './system/delete-api-key.rb'
|
||||||
require './system/get-all-keys.rb'
|
require './system/get-api-keys.rb'
|
||||||
require './system/file-upload-download.rb'
|
require './system/file-upload-download.rb'
|
||||||
require './system/csv-import.rb'
|
require './system/csv-import.rb'
|
||||||
require './system/disable-user-system.rb'
|
require './system/disable-user-system.rb'
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
describe'system/get-all-keys' do
|
describe'system/get-api-keys' do
|
||||||
request('/user/logout')
|
request('/user/logout')
|
||||||
Scripts.login($staff[:email], $staff[:password], true)
|
Scripts.login($staff[:email], $staff[:password], true)
|
||||||
|
|
||||||
it 'should get all API keys' do
|
it 'should get all API keys' do
|
||||||
Scripts.createAPIKey('namekey1')
|
Scripts.createAPIKey('namekey1')
|
||||||
Scripts.createAPIKey('namekey2')
|
Scripts.createAPIKey('namekey2')
|
||||||
Scripts.createAPIKey('namekey3')
|
Scripts.createAPIKey('namekey3')
|
||||||
Scripts.createAPIKey('namekey4')
|
Scripts.createAPIKey('namekey4')
|
||||||
Scripts.createAPIKey('namekey5')
|
Scripts.createAPIKey('namekey5')
|
||||||
|
|
||||||
result= request('/system/get-all-keys', {
|
result = request('/system/get-api-keys', {
|
||||||
csrf_userid: $csrf_userid,
|
csrf_userid: $csrf_userid,
|
||||||
csrf_token: $csrf_token,
|
csrf_token: $csrf_token,
|
||||||
})
|
})
|
Loading…
Reference in New Issue