Styles standardization (#934)
* WIP tag create and tag edit pop up * WIP admin panel email settings * WIP admin panel viwe article, article list, edit topic, add article * WIP admin panel list users and invite user widget * WIP admin panel view user * WIP admin panel custom fields and admin panel custom field form * WIP fix plus icon position and type * WIP admin panel custom responses * WIP admin panle my tickets and create ticket form * change check ticket button to green color * WIP dashboard edit profile page * Change icon in invite user button * WIP
This commit is contained in:
parent
3dd76f214d
commit
af3d95cf4d
|
@ -32,11 +32,11 @@ class ArticleAddModal extends React.Component {
|
|||
<Form onSubmit={this.onAddNewArticleFormSubmit.bind(this)} loading={this.state.loading}>
|
||||
<FormField name="title" label={i18n('TITLE')} field="input" fieldProps={{size: 'large'}} validation="TITLE" required/>
|
||||
<FormField name="content" label={i18n('CONTENT')} field="textarea" validation="TEXT_AREA" required fieldProps={{allowImages: this.props.allowAttachments}}/>
|
||||
<SubmitButton type="secondary">{i18n('ADD_ARTICLE')}</SubmitButton>
|
||||
<Button className="article-add-modal__cancel-button" type="link" onClick={(event) => {
|
||||
event.preventDefault();
|
||||
ModalContainer.closeModal();
|
||||
}}>{i18n('CANCEL')}</Button>
|
||||
<SubmitButton className="article-add-modal__submit-button" type="secondary">{i18n('ADD_ARTICLE')}</SubmitButton>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
width: 800px;
|
||||
|
||||
&__cancel-button {
|
||||
float: right;
|
||||
float: left;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
&__submit-button {
|
||||
float: right;
|
||||
}
|
||||
}
|
|
@ -36,11 +36,13 @@ class ArticlesList extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if(this.props.errored) {
|
||||
const { errored, loading } = this.props;
|
||||
|
||||
if(errored) {
|
||||
return <Message type="error">{i18n('ERROR_RETRIEVING_ARTICLES')}</Message>;
|
||||
}
|
||||
|
||||
return (this.props.loading) ? <Loading /> : this.renderContent();
|
||||
return loading ? <Loading /> : this.renderContent();
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
|
@ -53,17 +55,19 @@ class ArticlesList extends React.Component {
|
|||
}
|
||||
|
||||
renderTopics() {
|
||||
const { topics, editable, articlePath } = this.props;
|
||||
|
||||
return (
|
||||
<div className="articles-list__topics">
|
||||
{this.props.topics.map((topic, index) => {
|
||||
{topics.map((topic, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
<TopicViewer
|
||||
{...topic}
|
||||
id={topic.id * 1}
|
||||
editable={this.props.editable}
|
||||
editable={editable}
|
||||
onChange={this.retrieveArticles.bind(this)}
|
||||
articlePath={this.props.articlePath} />
|
||||
articlePath={articlePath} />
|
||||
<span className="separator" />
|
||||
</div>
|
||||
);
|
||||
|
@ -76,7 +80,7 @@ class ArticlesList extends React.Component {
|
|||
return (
|
||||
<div className="articles-list__add-topic-button">
|
||||
<Button onClick={() => ModalContainer.openModal(<TopicEditModal addForm onChange={this.retrieveArticles.bind(this)} />)} type="secondary" className="articles-list__add">
|
||||
<Icon name="plus-circle" size="2x" className="articles-list__add-icon"/> {i18n('ADD_TOPIC')}
|
||||
<Icon name="plus" className="articles-list__add-icon" /> {i18n('ADD_TOPIC')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
@ -88,9 +92,11 @@ class ArticlesList extends React.Component {
|
|||
}
|
||||
|
||||
export default connect((store) => {
|
||||
const { topics, errored, loading } = store.articles;
|
||||
|
||||
return {
|
||||
topics: store.articles.topics.map((topic) => {return {...topic, private: topic.private === "1"}}),
|
||||
errored: store.articles.errored,
|
||||
loading: store.articles.loading
|
||||
topics: topics.map((topic) => {return {...topic, private: topic.private === "1"}}),
|
||||
errored,
|
||||
loading
|
||||
};
|
||||
})(ArticlesList);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.articles-list {
|
||||
|
||||
&__add {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__add-icon {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
margin-top: -4px;
|
||||
&__add-topic-button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,15 +37,16 @@ class TopicEditModal extends React.Component {
|
|||
<FormField name="title" label={i18n('TITLE')} fieldProps={{size: 'large'}} validation="TITLE" required />
|
||||
<FormField name="icon" className="topic-edit-modal__icon" label={i18n('ICON')} decorator={IconSelector} />
|
||||
<FormField name="color" className="topic-edit-modal__color" label={i18n('COLOR')} decorator={ColorSelector} />
|
||||
<FormField className="topic-edit-modal__private" label={i18n('PRIVATE')} name="private" field="checkbox"/>
|
||||
<FormField className="topic-edit-modal__private" label={i18n('PRIVATE')} name="private" field="checkbox" />
|
||||
<InfoTooltip className="topic-edit-modal__private" text={i18n('PRIVATE_TOPIC_DESCRIPTION')} />
|
||||
|
||||
<SubmitButton className="topic-edit-modal__save-button" type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
<Button className="topic-edit-modal__discard-button" onClick={this.onDiscardClick.bind(this)} size="small">
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
<div className="topic-edit-modal__buttons-container">
|
||||
<Button className="topic-edit-modal__discard-button" onClick={this.onDiscardClick.bind(this)} size="small">
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
<SubmitButton className="topic-edit-modal__save-button" type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -13,9 +13,19 @@
|
|||
|
||||
}
|
||||
|
||||
&__discard-button {
|
||||
float: right;
|
||||
&__buttons-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px 0 0 0;
|
||||
}
|
||||
|
||||
&__discard-button {
|
||||
float: left;
|
||||
}
|
||||
|
||||
&__private {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
|
|
|
@ -66,12 +66,12 @@ class AdminPanelViewArticle extends React.Component {
|
|||
return (
|
||||
<div className="admin-panel-view-article__content">
|
||||
<div className="admin-panel-view-article__edit-buttons">
|
||||
<Button className="admin-panel-view-article__edit-button" size="medium" onClick={this.onEditClick.bind(this, article)} type="tertiary">
|
||||
{i18n('EDIT')}
|
||||
</Button>
|
||||
<Button size="medium" onClick={this.onDeleteClick.bind(this, article)}>
|
||||
{i18n('DELETE')}
|
||||
</Button>
|
||||
<Button className="admin-panel-view-article__edit-button" size="medium" onClick={this.onEditClick.bind(this, article)} type="tertiary">
|
||||
{i18n('EDIT')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="admin-panel-view-article__article">
|
||||
<Header title={article.title}/>
|
||||
|
@ -91,10 +91,10 @@ class AdminPanelViewArticle extends React.Component {
|
|||
return (
|
||||
<Form values={this.state.form} onChange={(form) => this.setState({form})} onSubmit={this.onFormSubmit.bind(this)}>
|
||||
<div className="admin-panel-view-article__buttons">
|
||||
<SubmitButton className="admin-panel-view-article__button" type="secondary" size="medium">{i18n('SAVE')}</SubmitButton>
|
||||
<Button className="admin-panel-view-article__button" size="medium" onClick={this.onFormCancel.bind(this)}>
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
<SubmitButton className="admin-panel-view-article__button" type="secondary" size="medium">{i18n('SAVE')}</SubmitButton>
|
||||
</div>
|
||||
<FormField name="title" label={i18n('TITLE')} />
|
||||
<FormField name="content" label={i18n('CONTENT')} field="textarea" validation="TEXT_AREA" required fieldProps={{allowImages: this.props.allowAttachments}}/>
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
}
|
||||
|
||||
&__edit-buttons {
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 200px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ import ColorSelector from 'core-components/color-selector';
|
|||
|
||||
class AdminPanelCustomTagsModal extends React.Component {
|
||||
static contextTypes = {
|
||||
closeModal: React.PropTypes.func
|
||||
closeModal: React.PropTypes.func,
|
||||
createTag: React.PropTypes.bool
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
|
@ -27,57 +28,50 @@ class AdminPanelCustomTagsModal extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
this.props.createTag ? this.renderCreateTagContent() : this.renderEditTagContent()
|
||||
this.renderTagContentPopUp(this.props.createTag)
|
||||
);
|
||||
}
|
||||
|
||||
renderEditTagContent() {
|
||||
return (
|
||||
<div className='admin-panel-custom-tags-modal'>
|
||||
<Header title={i18n('EDIT_CUSTOM_TAG')} description={i18n('DESCRIPTION_EDIT_CUSTOM_TAG')} />
|
||||
<Form
|
||||
values={this.state.form}
|
||||
onChange={this.onFormChange.bind(this)}
|
||||
onSubmit={this.onSubmitEditTag.bind(this)}
|
||||
errors={this.state.errors}
|
||||
onValidateErrors={errors => this.setState({errors})}
|
||||
loading={this.state.loading}>
|
||||
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}}/>
|
||||
<FormField name="color" label={i18n('COLOR')} decorator={ColorSelector} />
|
||||
<div className='admin-panel-custom-tags-modal__actions'>
|
||||
<SubmitButton type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
<Button onClick={this.onDiscardClick.bind(this)} size="small">
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
renderTagContentPopUp(create) {
|
||||
const {
|
||||
form,
|
||||
errors,
|
||||
loading,
|
||||
} = this.state;
|
||||
let title, description, nameRequired, submitFunction;
|
||||
|
||||
if(create) {
|
||||
title = i18n('ADD_CUSTOM_TAG');
|
||||
description = i18n('DESCRIPTION_ADD_CUSTOM_TAG');
|
||||
submitFunction = this.onSubmitNewTag.bind(this);
|
||||
nameRequired = true;
|
||||
} else {
|
||||
title = i18n('EDIT_CUSTOM_TAG');
|
||||
description = i18n('DESCRIPTION_EDIT_CUSTOM_TAG');
|
||||
nameRequired = false;
|
||||
submitFunction = this.onSubmitEditTag.bind(this);
|
||||
}
|
||||
|
||||
renderCreateTagContent() {
|
||||
return (
|
||||
<div className='admin-panel-custom-tags-modal'>
|
||||
<Header title={i18n('ADD_CUSTOM_TAG')} description={i18n('DESCRIPTION_ADD_CUSTOM_TAG')} />
|
||||
<Header title={title} description={description} />
|
||||
<Form
|
||||
values={this.state.form}
|
||||
values={form}
|
||||
onChange={this.onFormChange.bind(this)}
|
||||
onSubmit={this.onSubmitNewTag.bind(this)}
|
||||
errors={this.state.errors}
|
||||
onSubmit={submitFunction}
|
||||
errors={errors}
|
||||
onValidateErrors={errors => this.setState({errors})}
|
||||
loading={this.state.loading}>
|
||||
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} required />
|
||||
<FormField name="color" label={i18n('COLOR')} decorator={ColorSelector} />
|
||||
<div className='admin-panel-custom-tags-modal__actions'>
|
||||
<SubmitButton type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
<Button onClick={this.onDiscardClick.bind(this)} size="small">
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
</div>
|
||||
loading={loading}>
|
||||
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} required={nameRequired} />
|
||||
<FormField name="color" label={i18n('COLOR')} decorator={ColorSelector} />
|
||||
<div className='admin-panel-custom-tags-modal__actions'>
|
||||
<Button onClick={this.onDiscardClick.bind(this)} size="small">
|
||||
{i18n('CANCEL')}
|
||||
</Button>
|
||||
<SubmitButton type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
@ -118,7 +112,7 @@ class AdminPanelCustomTagsModal extends React.Component {
|
|||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onSubmitNewTag(form) {
|
||||
this.setState({
|
||||
loading: true
|
||||
|
|
|
@ -44,7 +44,7 @@ class AdminPanelCustomTags extends React.Component {
|
|||
<div className="admin-panel-custom-tags__content">
|
||||
<div>
|
||||
<Button onClick={this.openTagModal.bind(this)} type="secondary">
|
||||
{i18n('ADD_CUSTOM_TAG')}<Icon className="admin-panel-custom-tags__add-button-icon" name="plus"/>
|
||||
<Icon className="admin-panel-custom-tags__add-button-icon" name="plus" /> {i18n('ADD_CUSTOM_TAG')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="admin-panel-custom-tags__tag-list">
|
||||
|
@ -77,13 +77,13 @@ class AdminPanelCustomTags extends React.Component {
|
|||
|
||||
openEditTagModal(tagId,tagName,tagColor, event) {
|
||||
ModalContainer.openModal(
|
||||
<AdminPanelCustomTagsModal defaultValues={{name: tagName , color: tagColor}} id={tagId} onTagChange={this.retrieveCustomTags.bind(this)}/>
|
||||
<AdminPanelCustomTagsModal defaultValues={{name: tagName , color: tagColor}} id={tagId} onTagChange={this.retrieveCustomTags.bind(this)} />
|
||||
);
|
||||
}
|
||||
|
||||
onDeleteClick(tagId, event) {
|
||||
event.preventDefault();
|
||||
AreYouSure.openModal(i18n('WILL_DELETE_CUSTOM_RESPONSE'), this.deleteCustomTag.bind(this, tagId));
|
||||
AreYouSure.openModal(i18n('WILL_DELETE_CUSTOM_TAG'), this.deleteCustomTag.bind(this, tagId));
|
||||
}
|
||||
|
||||
deleteCustomTag(tagId) {
|
||||
|
|
|
@ -138,11 +138,12 @@ class AdminPanelEmailSettings extends React.Component {
|
|||
<FormField name="smtp-user" label={i18n('SMTP_USER')} fieldProps={{size: 'large'}} />
|
||||
<FormField name="smtp-pass" label={i18n('SMTP_PASSWORD')} fieldProps={{size: 'large', autoComplete: 'off'}} />
|
||||
<div className="admin-panel-email-settings__server-form-buttons">
|
||||
<SubmitButton className="admin-panel-email-settings__submit" type="secondary"
|
||||
size="small">{i18n('SAVE')}</SubmitButton>
|
||||
<SubmitButton type="tertiary" size="small" onClick={this.testSMTP.bind(this)}>
|
||||
{i18n('TEST')}
|
||||
</SubmitButton>
|
||||
<SubmitButton className="admin-panel-email-settings__submit" type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -160,11 +161,12 @@ class AdminPanelEmailSettings extends React.Component {
|
|||
infoMessage={i18n('IMAP_TOKEN_DESCRIPTION')}
|
||||
fieldProps={{size: 'large', icon: 'refresh', onIconClick: this.generateImapToken.bind(this)}} />
|
||||
<div className="admin-panel-email-settings__server-form-buttons">
|
||||
<SubmitButton className="admin-panel-email-settings__submit" type="secondary"
|
||||
size="small">{i18n('SAVE')}</SubmitButton>
|
||||
<SubmitButton type="tertiary" size="small" onClick={this.testIMAP.bind(this)}>
|
||||
{i18n('TEST')}
|
||||
</SubmitButton>
|
||||
<SubmitButton className="admin-panel-email-settings__submit" type="secondary" size="small">
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
<Message className="admin-panel-email-settings__imap-message" type="info">
|
||||
|
@ -177,10 +179,16 @@ class AdminPanelEmailSettings extends React.Component {
|
|||
}
|
||||
|
||||
renderForm() {
|
||||
const {
|
||||
form,
|
||||
language,
|
||||
selectedIndex,
|
||||
edited
|
||||
} = this.state;
|
||||
return (
|
||||
<div className="col-md-9">
|
||||
<FormField label={i18n('LANGUAGE')} decorator={LanguageSelector} value={this.state.language}
|
||||
onChange={event => this.onItemChange(this.state.selectedIndex, event.target.value)}
|
||||
<FormField label={i18n('LANGUAGE')} decorator={LanguageSelector} value={language}
|
||||
onChange={event => this.onItemChange(selectedIndex, event.target.value)}
|
||||
fieldProps={{
|
||||
type: 'allowed',
|
||||
size: 'medium'
|
||||
|
@ -196,29 +204,32 @@ class AdminPanelEmailSettings extends React.Component {
|
|||
<FormField key="text1" label={i18n('TEXT') + '1'} name="text1" validation="TEXT_AREA" required
|
||||
decorator={'textarea'}
|
||||
fieldProps={{className: 'admin-panel-email-settings__text-area'}} />
|
||||
{(this.state.form.text2) ?
|
||||
{(form.text2) ?
|
||||
<FormField key="text2" label={i18n('TEXT') + '2'} name="text2" validation="TEXT_AREA" required
|
||||
decorator={'textarea'}
|
||||
fieldProps={{className: 'admin-panel-email-settings__text-area'}} /> : null}
|
||||
{(this.state.form.text3) ?
|
||||
{(form.text3) ?
|
||||
<FormField key="text3" label={i18n('TEXT') + '3'} name="text3" validation="TEXT_AREA" required
|
||||
decorator={'textarea'}
|
||||
fieldProps={{className: 'admin-panel-email-settings__text-area'}} /> : null}
|
||||
|
||||
<div className="admin-panel-email-settings__actions">
|
||||
<div className="admin-panel-email-settings__save-button">
|
||||
<SubmitButton key="submit-email-template" type="secondary" size="small" onClick={e => {
|
||||
e.preventDefault();
|
||||
this.onFormSubmit(this.state.form);
|
||||
}}>{i18n('SAVE')}</SubmitButton>
|
||||
</div>
|
||||
<div className="admin-panel-email-settings__optional-buttons">
|
||||
{(this.state.edited) ? this.renderDiscardButton() : null}
|
||||
<div className="admin-panel-email-settings__recover-button">
|
||||
<Button onClick={this.onRecoverClick.bind(this)} size="medium">
|
||||
{i18n('RECOVER_DEFAULT')}
|
||||
</Button>
|
||||
</div>
|
||||
{edited ? this.renderDiscardButton() : null}
|
||||
</div>
|
||||
<div className="admin-panel-email-settings__save-button">
|
||||
<SubmitButton
|
||||
key="submit-email-template"
|
||||
type="secondary"
|
||||
size="small"
|
||||
onClick={(e) => {e.preventDefault(); this.onFormSubmit(form);}}>
|
||||
{i18n('SAVE')}
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
|
||||
&__save-button {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
float: right;
|
||||
}
|
||||
|
||||
&__optional-buttons {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
float: left;
|
||||
}
|
||||
|
||||
&__discard-button {
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
&__recover-button {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
&__image-container,
|
||||
|
|
|
@ -100,10 +100,10 @@ class AdminPanelSystemPreferences extends React.Component {
|
|||
</div>
|
||||
<div className="row admin-panel-system-preferences__container">
|
||||
<div className="col-md-4 col-md-offset-2">
|
||||
<SubmitButton className="admin-panel-system-preferences__container__button" type="secondary">{i18n('UPDATE_SETTINGS')}</SubmitButton>
|
||||
<Button className="admin-panel-system-preferences__container__button" onClick={this.onDiscardChangesSubmit.bind(this)}>{i18n('DISCARD_CHANGES')}</Button>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<Button className="admin-panel-system-preferences__container__button" onClick={this.onDiscardChangesSubmit.bind(this)}>{i18n('DISCARD_CHANGES')}</Button>
|
||||
<SubmitButton className="admin-panel-system-preferences__container__button" type="secondary">{i18n('UPDATE_SETTINGS')}</SubmitButton>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
|
|
@ -14,7 +14,6 @@ import ModalContainer from 'app-components/modal-container';
|
|||
import InviteStaffModal from 'app/admin/panel/staff/invite-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';
|
||||
|
@ -48,7 +47,7 @@ class AdminPanelStaffMembers extends React.Component {
|
|||
<div className="admin-panel-staff-members__wrapper">
|
||||
<DepartmentDropdown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" />
|
||||
<Button onClick={this.onInviteStaff.bind(this)} size="medium" type="secondary" className="admin-panel-staff-members__button">
|
||||
<Icon name="user-plus" className=""/> {i18n('INVITE_STAFF')}
|
||||
<Icon name="user-plus" className="" /> {i18n('INVITE_STAFF')}
|
||||
</Button>
|
||||
</div>
|
||||
{(this.props.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />}
|
||||
|
|
|
@ -61,7 +61,7 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
return (
|
||||
<div className="row">
|
||||
<div className="col-md-3">
|
||||
<Listing {...this.getListingProps()}/>
|
||||
<Listing {...this.getListingProps()} />
|
||||
</div>
|
||||
{this.state.showForm ? this.renderForm() : null}
|
||||
</div>
|
||||
|
@ -71,7 +71,7 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
renderLoading() {
|
||||
return (
|
||||
<div className="admin-panel-custom-responses__loading">
|
||||
<Loading backgrounded size="large"/>
|
||||
<Loading backgrounded size="large" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
<Form {...this.getFormProps()}>
|
||||
<div className="row">
|
||||
<div className="col-md-7">
|
||||
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required fieldProps={{size: 'large'}}/>
|
||||
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required fieldProps={{size: 'large'}} />
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<FormField label={i18n('LANGUAGE')} name="language" field="input" decorator={LanguageSelector} fieldProps={{size: 'medium'}} />
|
||||
|
@ -90,10 +90,10 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
</div>
|
||||
<FormField label={i18n('CONTENT')} name="content" validation="TEXT_AREA" required field="textarea" />
|
||||
<div className="admin-panel-custom-responses__actions">
|
||||
{(this.state.selectedIndex !== -1) ? this.renderOptionalButtons() : null}
|
||||
<div className="admin-panel-custom-responses__save-button">
|
||||
<SubmitButton type="secondary" size="small">{i18n('SAVE')}</SubmitButton>
|
||||
</div>
|
||||
{(this.state.selectedIndex !== -1) ? this.renderOptionalButtons() : null}
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -103,12 +103,12 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
renderOptionalButtons() {
|
||||
return (
|
||||
<div className="admin-panel-custom-responses__optional-buttons">
|
||||
<div className="admin-panel-custom-responses__discard-button">
|
||||
{this.isEdited() ? <Button onClick={this.onDiscardChangesClick.bind(this)}>{i18n('DISCARD_CHANGES')}</Button> : null}
|
||||
</div>
|
||||
<div className="admin-panel-custom-responses__delete-button">
|
||||
<Button onClick={this.onDeleteClick.bind(this)}>{i18n('DELETE')}</Button>
|
||||
</div>
|
||||
<div className="admin-panel-custom-responses__discard-button">
|
||||
{this.isEdited() ? <Button onClick={this.onDiscardChangesClick.bind(this)}>{i18n('DISCARD_CHANGES')}</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -125,10 +125,16 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
}
|
||||
|
||||
getFormProps() {
|
||||
const {
|
||||
form,
|
||||
errors,
|
||||
formLoading
|
||||
} = this.state;
|
||||
|
||||
return {
|
||||
values: this.state.form,
|
||||
errors: this.state.errors,
|
||||
loading: this.state.formLoading,
|
||||
values: form,
|
||||
errors,
|
||||
loading: formLoading,
|
||||
onClick: () => this.setState({formClicked: true}),
|
||||
onChange: (form) => this.setState({form}),
|
||||
onValidateErrors: (errors) => {this.setState({errors})},
|
||||
|
@ -143,7 +149,7 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
<span>
|
||||
{item.name}
|
||||
<span className="admin-panel-custom-responses__item-flag">
|
||||
<Icon name={(item.language != 'en') ? item.language : 'us'}/>
|
||||
<Icon name={(item.language != 'en') ? item.language : 'us'} />
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
|
@ -161,13 +167,15 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
|
||||
onFormSubmit(form) {
|
||||
const {items, allowedLanguages} = this.props;
|
||||
const { selectedIndex } = this.state;
|
||||
|
||||
this.setState({formLoading: true});
|
||||
|
||||
if(this.state.selectedIndex !== -1) {
|
||||
if(selectedIndex !== -1) {
|
||||
API.call({
|
||||
path: '/ticket/edit-custom-response',
|
||||
data: {
|
||||
id: items[this.state.selectedIndex].id,
|
||||
id: items[selectedIndex].id,
|
||||
name: form.title,
|
||||
content: form.content,
|
||||
language: _.includes(allowedLanguages, form.language) ? form.language : allowedLanguages[0]
|
||||
|
@ -215,11 +223,17 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
}
|
||||
|
||||
updateForm(index) {
|
||||
const {
|
||||
items,
|
||||
language
|
||||
} = this.props;
|
||||
const item = items[index];
|
||||
|
||||
let form = _.clone(this.state.form);
|
||||
|
||||
form.title = (this.props.items[index] && this.props.items[index].name) || '';
|
||||
form.content = TextEditor.getEditorStateFromHTML((this.props.items[index] && this.props.items[index].content) || '');
|
||||
form.language = (this.props.items[index] && this.props.items[index].language) || this.props.language;
|
||||
form.title = (item && item.name) || '';
|
||||
form.content = TextEditor.getEditorStateFromHTML((item && item.content) || '');
|
||||
form.language = (item && item.language) || language;
|
||||
|
||||
this.setState({
|
||||
formClicked: false,
|
||||
|
@ -227,7 +241,7 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
selectedIndex: index,
|
||||
formLoading: false,
|
||||
originalForm: form,
|
||||
form: form,
|
||||
form,
|
||||
errors: {}
|
||||
});
|
||||
}
|
||||
|
@ -237,10 +251,18 @@ class AdminPanelCustomResponses extends React.Component {
|
|||
}
|
||||
|
||||
isEdited() {
|
||||
return this.state.form.title && this.state.formClicked && (
|
||||
this.state.form.title != this.state.originalForm.title ||
|
||||
this.state.form.content != this.state.originalForm.content ||
|
||||
this.state.form.language != this.state.originalForm.language
|
||||
const {
|
||||
form,
|
||||
formClicked,
|
||||
originalForm
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
form.title && formClicked && (
|
||||
form.title != originalForm.title ||
|
||||
form.content != originalForm.content ||
|
||||
form.language != originalForm.language
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,25 +7,25 @@
|
|||
float: right;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
text-align: left;
|
||||
&__actions,
|
||||
&__optional-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__discard-button,
|
||||
&__delete-button {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&__save-button {
|
||||
display: inline-block;
|
||||
margin-right: 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__optional-buttons {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
&__discard-button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__delete-button {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,10 +36,10 @@ class AdminPanelMyTickets extends React.Component {
|
|||
return (
|
||||
<div className="admin-panel-my-tickets">
|
||||
<Header title={i18n('MY_TICKETS')} description={i18n('MY_TICKETS_DESCRIPTION')} />
|
||||
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()}/>}
|
||||
{(this.props.error) ? <Message type="error">{i18n('ERROR_RETRIEVING_TICKETS')}</Message> : <TicketList {...this.getProps()} />}
|
||||
<div style={{textAlign: 'right', marginTop: 10}}>
|
||||
<Button onClick={this.onCreateTicket.bind(this)} type="secondary" size="medium">
|
||||
<Icon size="sm" name="plus"/> {i18n('CREATE_TICKET')}
|
||||
<Icon size="sm" name="plus" /> {i18n('CREATE_TICKET')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -47,21 +47,31 @@ class AdminPanelMyTickets extends React.Component {
|
|||
}
|
||||
|
||||
getProps() {
|
||||
const { closedTicketsShown } = this.state;
|
||||
const {
|
||||
userId,
|
||||
departments,
|
||||
tickets,
|
||||
loading,
|
||||
pages,
|
||||
page
|
||||
} = this.props;
|
||||
|
||||
return {
|
||||
userId: this.props.userId,
|
||||
departments: this.props.departments,
|
||||
tickets: this.props.tickets,
|
||||
userId,
|
||||
departments ,
|
||||
tickets,
|
||||
type: 'secondary',
|
||||
loading: this.props.loading,
|
||||
loading,
|
||||
ticketPath: '/admin/panel/tickets/view-ticket/',
|
||||
closedTicketsShown: this.state.closedTicketsShown,
|
||||
closedTicketsShown,
|
||||
onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this),
|
||||
pages: this.props.pages,
|
||||
page: this.props.page,
|
||||
pages,
|
||||
page,
|
||||
onPageChange: event => this.retrieveMyTickets(event.target.value),
|
||||
onDepartmentChange: departmentId => {
|
||||
this.setState({departmentId});
|
||||
this.retrieveMyTickets(1, this.state.closedTicketsShown, departmentId);
|
||||
this.retrieveMyTickets(1, closedTicketsShown, departmentId);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -76,10 +86,13 @@ class AdminPanelMyTickets extends React.Component {
|
|||
|
||||
onCreateTicket() {
|
||||
ModalContainer.openModal(
|
||||
<div>
|
||||
<CreateTicketForm isStaff={true} onSuccess={this.onCreateTicketSuccess.bind(this)} />
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
||||
<div className="admin-panel-my-tickets__create-ticket-form-container">
|
||||
<CreateTicketForm
|
||||
className="admin-panel-my-tickets__create-ticket-form"
|
||||
isStaff={true}
|
||||
onSuccess={this.onCreateTicketSuccess.bind(this)} />
|
||||
<div className="admin-panel-my-tickets__close-button-container">
|
||||
<Button className="admin-panel-my-tickets__close-button" onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
.admin-panel-my-tickets {
|
||||
&__close-button-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,24 +26,30 @@ class AdminPanelCustomFieldForm extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
loading,
|
||||
addForm,
|
||||
error
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<div className="admin-panel-custom-field-form">
|
||||
<Header title={i18n('NEW_CUSTOM_FIELD')} description={i18n('NEW_CUSTOM_FIELD_DESCRIPTION')} />
|
||||
<div className="admin-panel-custom-field-form__form-container">
|
||||
<Form
|
||||
className="admin-panel-custom-field-form__form"
|
||||
loading={this.state.loading}
|
||||
values={this.state.addForm}
|
||||
loading={loading}
|
||||
values={addForm}
|
||||
onChange={this.onAddFormChange.bind(this)}
|
||||
onSubmit={this.onSubmit.bind(this)}>
|
||||
<FormField name="name" validation="NAME" label={i18n('NAME')} field="input" fieldProps={{size: 'large'}} required/>
|
||||
<FormField name="description" label={i18n('FIELD_DESCRIPTION')} field="input" fieldProps={{size: 'large'}}/>
|
||||
<FormField name="type" label={i18n('TYPE')} field="select" fieldProps={{size: 'large', items: [{content: i18n('TEXT_INPUT')}, {content: i18n('SELECT_INPUT')}]}} required/>
|
||||
{this.state.addForm.type ? this.renderOptionFormFields() : null}
|
||||
{this.state.error ? this.renderErrorMessage() : null}
|
||||
{addForm.type ? this.renderOptionFormFields() : null}
|
||||
{error ? this.renderErrorMessage() : null}
|
||||
<div className="admin-panel-custom-field-form__buttons">
|
||||
<SubmitButton>{i18n('SUBMIT')}</SubmitButton>
|
||||
<Button onClick={this.props.onClose} type="link">{i18n('CLOSE')}</Button>
|
||||
<SubmitButton type="secondary">{i18n('SUBMIT')}</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
@ -29,9 +29,9 @@ class AdminPanelCustomFields extends React.Component {
|
|||
<div className="admin-panel-custom-fields">
|
||||
<Header title={i18n('CUSTOM_FIELDS')} description={i18n('CUSTOM_FIELDS_DESCRIPTION')} />
|
||||
{this.renderCustomFieldList()}
|
||||
<div className="admin-panel-custom-fields__add-button">
|
||||
<Button type="secondary" onClick={this.onNewCustomFieldClick.bind(this)}>
|
||||
<Icon name="plus"/> {i18n('NEW_CUSTOM_FIELD')}
|
||||
<div className="admin-panel-custom-fields__container-button">
|
||||
<Button className="admin-panel-custom-fields__container-button__add-button" type="secondary" onClick={this.onNewCustomFieldClick.bind(this)}>
|
||||
<Icon name="plus" /> {i18n('NEW_CUSTOM_FIELD')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,8 +48,7 @@ class AdminPanelCustomFields extends React.Component {
|
|||
{key: 'options', value: i18n('OPTIONS')},
|
||||
{key: 'actions', value: ''},
|
||||
]}
|
||||
rows={this.state.customFields.map(this.getCustomField.bind(this))}
|
||||
/>
|
||||
rows={this.state.customFields.map(this.getCustomField.bind(this))} />
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -72,11 +71,11 @@ class AdminPanelCustomFields extends React.Component {
|
|||
onNewCustomFieldClick() {
|
||||
ModalContainer.openModal(
|
||||
<AdminPanelCustomFieldForm
|
||||
onClose={ModalContainer.closeModal}
|
||||
onClose={(e) => {e.preventDefault(); ModalContainer.closeModal();}}
|
||||
onChange={() => {
|
||||
this.retrieveCustomFields();
|
||||
ModalContainer.closeModal();
|
||||
}}/>
|
||||
}} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,14 @@
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
&__add-button {
|
||||
text-align: left;
|
||||
margin-top: 14px;
|
||||
&__container-button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
|
||||
&__add-button {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,10 +48,10 @@ class AdminPanelListUsers extends React.Component {
|
|||
return (
|
||||
<div>
|
||||
<SearchBox className="admin-panel-list-users__search-box" placeholder={i18n('SEARCH_USERS')} onSearch={this.onSearch.bind(this)} />
|
||||
<Table {...this.getTableProps()}/>
|
||||
<Table {...this.getTableProps()} />
|
||||
<div style={{textAlign: 'right', marginTop: 10}}>
|
||||
<Button onClick={this.onInviteUser.bind(this)} type="secondary" size="medium">
|
||||
<Icon size="sm" name="plus"/> {i18n('INVITE_USER')}
|
||||
<Icon size="sm" name="user-plus" /> {i18n('INVITE_USER')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -138,11 +138,17 @@ class AdminPanelListUsers extends React.Component {
|
|||
}
|
||||
|
||||
onPageChange(event) {
|
||||
const {
|
||||
orderBy,
|
||||
desc,
|
||||
search
|
||||
} = this.state;
|
||||
|
||||
this.retrieveUsers({
|
||||
page: event.target.value,
|
||||
orderBy: this.state.orderBy,
|
||||
desc: this.state.desc,
|
||||
search: this.state.search
|
||||
orderBy,
|
||||
desc,
|
||||
search
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -179,9 +185,6 @@ class AdminPanelListUsers extends React.Component {
|
|||
ModalContainer.openModal(
|
||||
<div className="admin-panel-list-users__invite-user-form">
|
||||
<InviteUserWidget onSuccess={this.onInviteUserSuccess.bind(this)} />
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -51,31 +51,40 @@ class AdminPanelViewUser extends React.Component {
|
|||
}
|
||||
|
||||
renderUserInfo() {
|
||||
const {
|
||||
name,
|
||||
disabled,
|
||||
email,
|
||||
verified,
|
||||
customfields,
|
||||
loading
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<div className="admin-panel-view-user__content">
|
||||
<div className="admin-panel-view-user__info">
|
||||
<div className="admin-panel-view-user__info-item">
|
||||
{i18n('NAME')}
|
||||
<div className="admin-panel-view-user__info-box">
|
||||
{this.state.name}
|
||||
{(this.state.disabled) ? this.renderDisabled() : null}
|
||||
{name}
|
||||
{disabled ? this.renderDisabled() : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="admin-panel-view-user__info-item">
|
||||
{i18n('EMAIL')}
|
||||
<div className="admin-panel-view-user__info-box">
|
||||
{this.state.email}
|
||||
{(!this.state.verified) ? this.renderNotVerified() : null}
|
||||
{email}
|
||||
{(!verified) ? this.renderNotVerified() : null}
|
||||
</div>
|
||||
</div>
|
||||
{this.state.customfields.map(this.renderCustomField.bind(this))}
|
||||
{customfields.map(this.renderCustomField.bind(this))}
|
||||
<div className="admin-panel-view-user__action-buttons">
|
||||
<Button
|
||||
className="admin-panel-view-user__action-button"
|
||||
onClick={this.onDisableClick.bind(this)}
|
||||
size="medium"
|
||||
type={this.state.disabled ? 'tertiary' : 'primary'}>
|
||||
{i18n(this.state.disabled ? 'ENABLE_USER' : 'DISABLE_USER')}
|
||||
type={disabled ? 'tertiary' : 'primary'}>
|
||||
{i18n(disabled ? 'ENABLE_USER' : 'DISABLE_USER')}
|
||||
</Button>
|
||||
<Button className="admin-panel-view-user__action-button" onClick={this.onDeleteClick.bind(this)} size="medium">
|
||||
{i18n('DELETE_AND_BAN')}
|
||||
|
@ -89,15 +98,14 @@ class AdminPanelViewUser extends React.Component {
|
|||
<Autocomplete
|
||||
onChange={this.onChangeValues.bind(this)}
|
||||
getItemListFromQuery={this.searchUsers.bind(this)}
|
||||
values={this.transformUserListToAutocomplete()}
|
||||
/>
|
||||
values={this.transformUserListToAutocomplete()} />
|
||||
<Button
|
||||
disabled = {this.state.loading}
|
||||
disabled={loading}
|
||||
type="secondary"
|
||||
className="admin-panel-view-user__submit-button"
|
||||
onClick={this.onClickSupervisorUserButton.bind(this)}
|
||||
size="medium"
|
||||
>
|
||||
{i18n('UPDATE')}
|
||||
size="medium">
|
||||
{i18n('UPDATE')}
|
||||
</Button>
|
||||
</div>
|
||||
{this.renderSupervisedUserMessage()}
|
||||
|
@ -105,26 +113,28 @@ class AdminPanelViewUser extends React.Component {
|
|||
<span className="separator" />
|
||||
<div className="admin-panel-view-user__tickets">
|
||||
<div className="admin-panel-view-user__tickets-title">{i18n('TICKETS')}</div>
|
||||
<TicketList {...this.getTicketListProps()}/>
|
||||
<TicketList {...this.getTicketListProps()} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSupervisedUserMessage(){
|
||||
if(this.state.message) {
|
||||
if(this.state.message != 'success'){
|
||||
return(
|
||||
const { message } = this.state;
|
||||
|
||||
if(message) {
|
||||
if(message != 'success') {
|
||||
return (
|
||||
<div className="admin-panel-view-user__supervised-users-message">
|
||||
<Message type="error">{i18n(this.state.message)}</Message>
|
||||
<Message type="error">{i18n(message)}</Message>
|
||||
</div>
|
||||
)
|
||||
}else{
|
||||
return(
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className= "admin-panel-view-user__supervised-users-message">
|
||||
<Message type="success">{i18n('SUPERVISED_USERS_UPDATED')}</Message>
|
||||
<Message type="success">{i18n('SUPERVISED_USERS_UPDATED')}</Message>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,11 +143,11 @@ class AdminPanelViewUser extends React.Component {
|
|||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
|
||||
|
||||
const userIdList = this.state.userList.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
|
||||
|
||||
API.call({
|
||||
path: '/user/edit-supervised-list',
|
||||
data: {
|
||||
|
@ -177,11 +187,16 @@ class AdminPanelViewUser extends React.Component {
|
|||
const authorsListWithoutMe = r.data.authors.filter(author => author.id != this.props.params.userId);
|
||||
|
||||
return authorsListWithoutMe.map(author => {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = author;
|
||||
|
||||
return {
|
||||
name: author.name,
|
||||
name,
|
||||
color: "gray",
|
||||
id: author.id*1,
|
||||
content: <div>{author.name}</div>,
|
||||
id: id*1,
|
||||
content: <div>{name}</div>,
|
||||
isStaff: false
|
||||
}});
|
||||
}).catch((r) => {
|
||||
|
@ -192,10 +207,15 @@ class AdminPanelViewUser extends React.Component {
|
|||
transformUserListToAutocomplete() {
|
||||
return(
|
||||
this.state.userList.map((user) => {
|
||||
const {
|
||||
id,
|
||||
name
|
||||
} = user;
|
||||
|
||||
return ({
|
||||
id: user.id*1,
|
||||
name: user.name,
|
||||
content: <div>{user.name}</div>,
|
||||
id: id*1,
|
||||
name,
|
||||
content: <div>{name}</div>,
|
||||
color: 'grey',
|
||||
isStaff: false
|
||||
});
|
||||
|
@ -215,37 +235,58 @@ class AdminPanelViewUser extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderCustomField(customfield) {
|
||||
renderCustomField(_customfield) {
|
||||
const {
|
||||
customfield,
|
||||
value,
|
||||
id
|
||||
} = _customfield;
|
||||
|
||||
return (
|
||||
<div className="admin-panel-view-user__info-item">
|
||||
{customfield.customfield}
|
||||
<div className="admin-panel-view-user__info-item" key={`customFieldId__${id}`}>
|
||||
{customfield}
|
||||
<div className="admin-panel-view-user__info-box">
|
||||
{customfield.value}
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getTicketListProps() {
|
||||
const {
|
||||
tickets,
|
||||
loading
|
||||
} = this.state;
|
||||
|
||||
return {
|
||||
type: 'secondary',
|
||||
tickets: this.state.tickets,
|
||||
loading: this.state.loading,
|
||||
tickets,
|
||||
loading,
|
||||
departments: this.props.departments,
|
||||
ticketPath: '/admin/panel/tickets/view-ticket/'
|
||||
};
|
||||
}
|
||||
|
||||
onUserRetrieved(result) {
|
||||
const {
|
||||
name,
|
||||
email,
|
||||
verified,
|
||||
tickets,
|
||||
disabled,
|
||||
customfields,
|
||||
userList
|
||||
} = result.data;
|
||||
|
||||
this.setState({
|
||||
name: result.data.name,
|
||||
email: result.data.email,
|
||||
verified: result.data.verified,
|
||||
tickets: result.data.tickets,
|
||||
disabled: result.data.disabled,
|
||||
customfields: result.data.customfields,
|
||||
name,
|
||||
email,
|
||||
verified,
|
||||
tickets,
|
||||
disabled,
|
||||
customfields,
|
||||
loading: false,
|
||||
userList: result.data.userList
|
||||
userList
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
@ -13,6 +12,8 @@ import Form from 'core-components/form';
|
|||
import FormField from 'core-components/form-field';
|
||||
import Widget from 'core-components/widget';
|
||||
import Header from 'core-components/header';
|
||||
import Button from 'core-components/button';
|
||||
import ModalContainer from 'app-components/modal-container';
|
||||
|
||||
class InviteUserWidget extends React.Component {
|
||||
|
||||
|
@ -52,9 +53,11 @@ class InviteUserWidget extends React.Component {
|
|||
<div className="invite-user-widget__captcha">
|
||||
<Captcha ref="captcha"/>
|
||||
</div>
|
||||
<SubmitButton type="primary">{i18n('INVITE_USER')}</SubmitButton>
|
||||
<div className="invite-user-widget__buttons-container">
|
||||
<Button onClick={(e) => {e.preventDefault(); ModalContainer.closeModal();}} type="link">{i18n('CANCEL')}</Button>
|
||||
<SubmitButton type="secondary">{i18n('INVITE_USER')}</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
{this.renderMessage()}
|
||||
</Widget>
|
||||
);
|
||||
|
@ -88,9 +91,9 @@ class InviteUserWidget extends React.Component {
|
|||
renderMessage() {
|
||||
switch (this.state.message) {
|
||||
case 'success':
|
||||
return <Message type="success">{i18n('INVITE_USER_SUCCESS')}</Message>;
|
||||
return <Message className="invite-user-widget__success-message" type="success">{i18n('INVITE_USER_SUCCESS')}</Message>;
|
||||
case 'fail':
|
||||
return <Message type="error">{i18n('EMAIL_EXISTS')}</Message>;
|
||||
return <Message className="invite-user-widget__error-message" type="error">{i18n('EMAIL_EXISTS')}</Message>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
.invite-user-widget {
|
||||
padding: 30px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
|
||||
&__form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__inputs {
|
||||
|
@ -16,4 +15,16 @@
|
|||
height: 78px;
|
||||
width: 304px;
|
||||
}
|
||||
|
||||
&__buttons-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__success-message,
|
||||
&__error-message {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import _ from 'lodash';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import history from 'lib-app/history';
|
||||
import i18n from 'lib-app/i18n';
|
||||
import API from 'lib-app/api-call';
|
||||
import SessionStore from 'lib-app/session-store';
|
||||
|
@ -45,36 +44,46 @@ class CreateTicketForm extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
userLogged,
|
||||
isDefaultDepartmentLocked,
|
||||
isStaff,
|
||||
onlyOneSupportedLanguage,
|
||||
allowAttachments
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className="create-ticket-form">
|
||||
<Header title={i18n('CREATE_TICKET')} description={i18n('CREATE_TICKET_DESCRIPTION')} />
|
||||
<Form {...this.getFormProps()}>
|
||||
{(!this.props.userLogged) ? this.renderEmailAndName() : null}
|
||||
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}}/>
|
||||
{(!userLogged) ? this.renderEmailAndName() : null}
|
||||
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}} />
|
||||
<div className="row">
|
||||
{!(this.props.isDefaultDepartmentLocked*1) || this.props.isStaff ?
|
||||
{!(isDefaultDepartmentLocked*1) || isStaff ?
|
||||
<FormField className="col-md-5" label={i18n('DEPARTMENT')} name="departmentIndex" field="select" decorator={DepartmentDropdown} fieldProps={{
|
||||
departments: SessionStore.getDepartments(),
|
||||
size: 'medium'
|
||||
}} /> : null
|
||||
}
|
||||
{!this.props.onlyOneSupportedLanguage ?
|
||||
{!onlyOneSupportedLanguage ?
|
||||
<FormField className="col-md-5" label={i18n('LANGUAGE')} name="language" field="select" decorator={LanguageSelector} fieldProps={{
|
||||
type: 'supported',
|
||||
size: 'medium'
|
||||
}}/> : null
|
||||
}} /> : null
|
||||
}
|
||||
</div>
|
||||
<FormField
|
||||
label={i18n('CONTENT')}
|
||||
name="content"
|
||||
validation="TEXT_AREA"
|
||||
fieldProps={{allowImages: this.props.allowAttachments}}
|
||||
fieldProps={{allowImages: allowAttachments}}
|
||||
required
|
||||
field="textarea" />
|
||||
{(this.props.allowAttachments) ? this.renderFileUpload() : null}
|
||||
{(!this.props.userLogged) ? this.renderCaptcha() : null}
|
||||
<SubmitButton>{i18n('CREATE_TICKET')}</SubmitButton>
|
||||
<div className="create-ticket-form__buttons-container">
|
||||
{allowAttachments ? this.renderFileUpload() : null}
|
||||
{(!userLogged) ? this.renderCaptcha() : null}
|
||||
<SubmitButton type="secondary">{i18n('CREATE_TICKET')}</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
{this.renderMessage()}
|
||||
</div>
|
||||
|
@ -84,8 +93,8 @@ class CreateTicketForm extends React.Component {
|
|||
renderEmailAndName() {
|
||||
return (
|
||||
<div className="row">
|
||||
<FormField className="col-md-6" label={i18n('EMAIL')} name="email" validation="EMAIL" required field="input" fieldProps={{size: 'large'}}/>
|
||||
<FormField className="col-md-6" label={i18n('FULL_NAME')} name="name" validation="NAME" required field="input" fieldProps={{size: 'large'}}/>
|
||||
<FormField className="col-md-6" label={i18n('EMAIL')} name="email" validation="EMAIL" required field="input" fieldProps={{size: 'large'}} />
|
||||
<FormField className="col-md-6" label={i18n('FULL_NAME')} name="name" validation="NAME" required field="input" fieldProps={{size: 'large'}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -101,7 +110,7 @@ class CreateTicketForm extends React.Component {
|
|||
renderCaptcha() {
|
||||
return (
|
||||
<div className="create-ticket-form__captcha">
|
||||
<Captcha ref="captcha"/>
|
||||
<Captcha ref="captcha" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -118,10 +127,15 @@ class CreateTicketForm extends React.Component {
|
|||
}
|
||||
|
||||
getFormProps() {
|
||||
const {
|
||||
loading,
|
||||
form
|
||||
} = this.state;
|
||||
|
||||
return {
|
||||
loading: this.state.loading,
|
||||
loading,
|
||||
onSubmit: this.onSubmit.bind(this),
|
||||
values: this.state.form,
|
||||
values: form,
|
||||
onChange: form => this.setState({form})
|
||||
};
|
||||
}
|
||||
|
@ -148,15 +162,16 @@ class CreateTicketForm extends React.Component {
|
|||
}
|
||||
|
||||
onTicketSuccess(email, result) {
|
||||
let message = 'success'
|
||||
this.setState({
|
||||
loading: false,
|
||||
message: message
|
||||
}, () => {
|
||||
if(this.props.onSuccess) {
|
||||
this.props.onSuccess(result, email, message);
|
||||
}
|
||||
});
|
||||
const { onSuccess } = this.props;
|
||||
const message = 'success';
|
||||
|
||||
this.setState(
|
||||
{
|
||||
loading: false,
|
||||
message
|
||||
},
|
||||
() => {onSuccess && onSuccess(result, email, message);}
|
||||
);
|
||||
}
|
||||
|
||||
onTicketFail() {
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__buttons-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__captcha {
|
||||
margin: 0 auto 20px;
|
||||
height: 78px;
|
||||
|
|
|
@ -43,54 +43,97 @@ class DashboardEditProfilePage extends React.Component {
|
|||
return (
|
||||
<div className="edit-profile-page">
|
||||
<Header title={i18n('EDIT_PROFILE')} description={i18n('EDIT_PROFILE_VIEW_DESCRIPTION')} />
|
||||
<div className="edit-profile-page__title">{i18n('EDIT_EMAIL')}</div>
|
||||
<Form loading={this.state.loadingEmail} onSubmit={this.onSubmitEditEmail.bind(this)}>
|
||||
<FormField name="newEmail" label={i18n('NEW_EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required/>
|
||||
<SubmitButton>{i18n('CHANGE_EMAIL')}</SubmitButton>
|
||||
{this.renderMessageEmail()}
|
||||
</Form>
|
||||
<div className="edit-profile-page__title">{i18n('EDIT_PASSWORD')}</div>
|
||||
<Form loading={this.state.loadingPass} onSubmit={this.onSubmitEditPassword.bind(this)}>
|
||||
<FormField name="oldPassword" label={i18n('OLD_PASSWORD')} field="input" validation="PASSWORD" fieldProps={{password:true, size:'large'}} required/>
|
||||
<FormField name="password" label={i18n('NEW_PASSWORD')} field="input" validation="PASSWORD" fieldProps={{password:true, size:'large'}} required/>
|
||||
<FormField name="repeatNewPassword" label={i18n('REPEAT_NEW_PASSWORD')} field="input" validation="REPEAT_PASSWORD" fieldProps={{password:true ,size:'large'}} required/>
|
||||
<SubmitButton>{i18n('CHANGE_PASSWORD')}</SubmitButton>
|
||||
{this.renderMessagePass()}
|
||||
</Form>
|
||||
{this.renderEditEmail()}
|
||||
{this.renderEditPassword()}
|
||||
{this.state.customFields.length ? this.renderCustomFields() : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCustomFields() {
|
||||
renderEditEmail() {
|
||||
return (
|
||||
<div>
|
||||
<div className="edit-profile-page__edit-email">
|
||||
<span className="separator" />
|
||||
<div className="edit-profile-page__title">{i18n('EDIT_EMAIL')}</div>
|
||||
<Form loading={this.state.loadingEmail} onSubmit={this.onSubmitEditEmail.bind(this)}>
|
||||
<div className="edit-profile-page__change-email-container">
|
||||
<FormField name="newEmail" label={i18n('NEW_EMAIL')} field="input" validation="EMAIL" fieldProps={{size: 'large'}} required />
|
||||
<SubmitButton type="secondary">{i18n('CHANGE_EMAIL')}</SubmitButton>
|
||||
</div>
|
||||
{this.renderMessageEmail()}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEditPassword() {
|
||||
return (
|
||||
<div className="edit-profile-page__edit-password">
|
||||
<span className="separator" />
|
||||
<div className="edit-profile-page__title">{i18n('EDIT_PASSWORD')}</div>
|
||||
<Form loading={this.state.loadingPass} onSubmit={this.onSubmitEditPassword.bind(this)}>
|
||||
<div className="edit-profile-page__change-password-container">
|
||||
<div className="edit-profile-page__change-password-form-fields">
|
||||
<FormField name="oldPassword" label={i18n('OLD_PASSWORD')} field="input" validation="PASSWORD" fieldProps={{password: true, size: 'large'}} required />
|
||||
<FormField name="password" label={i18n('NEW_PASSWORD')} field="input" validation="PASSWORD" fieldProps={{password: true, size: 'large'}} required />
|
||||
<FormField name="repeatNewPassword" label={i18n('REPEAT_NEW_PASSWORD')} field="input" validation="REPEAT_PASSWORD" fieldProps={{password: true, size: 'large'}} required />
|
||||
</div>
|
||||
<SubmitButton type="secondary">{i18n('CHANGE_PASSWORD')}</SubmitButton>
|
||||
</div>
|
||||
{this.renderMessagePass()}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCustomFields() {
|
||||
const {
|
||||
loadingCustomFields,
|
||||
customFieldsFrom,
|
||||
customFields
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<div className="edit-profile-page__edit-custom-field">
|
||||
<span className="separator" />
|
||||
<div className="edit-profile-page__title">{i18n('ADDITIONAL_FIELDS')}</div>
|
||||
<Form loading={this.state.loadingCustomFields} values={this.state.customFieldsFrom} onChange={form => this.setState({customFieldsFrom: form})} onSubmit={this.onCustomFieldsSubmit.bind(this)}>
|
||||
<div className="edit-profile-page__custom-fields">
|
||||
{this.state.customFields.map(this.renderCustomField.bind(this))}
|
||||
</div>
|
||||
<div className="row">
|
||||
<SubmitButton>{i18n('SAVE')}</SubmitButton>
|
||||
</div>
|
||||
<Form
|
||||
className="edit-profile-page__edit-custom-field-form"
|
||||
loading={loadingCustomFields}
|
||||
values={customFieldsFrom}
|
||||
onChange={form => this.setState({customFieldsFrom: form})}
|
||||
onSubmit={this.onCustomFieldsSubmit.bind(this)}>
|
||||
<div className="edit-profile-page__custom-fields">
|
||||
{customFields.map(this.renderCustomField.bind(this))}
|
||||
</div>
|
||||
<div className="edit-profile-page__update-custom-fields-button-container">
|
||||
<SubmitButton className="edit-profile-page__update-custom-fields-button" type="secondary">{i18n('UPDATE_CUSTOM_FIELDS')}</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderCustomField(customField, key) {
|
||||
if(customField.type === 'text') {
|
||||
const {
|
||||
type,
|
||||
name,
|
||||
description,
|
||||
options
|
||||
} = customField;
|
||||
|
||||
if(type === 'text') {
|
||||
return (
|
||||
<div className="edit-profile-page__custom-field" key={key}>
|
||||
<FormField name={customField.name} label={customField.name} infoMessage={customField.description} field="input" fieldProps={{size:'small'}}/>
|
||||
<FormField name={name} label={name} infoMessage={description} field="input" fieldProps={{size: 'small'}} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const items = customField.options.map(option => ({content: option.name, value: option.name}));
|
||||
const items = options.map(option => ({content: option.name, value: option.name}));
|
||||
|
||||
return (
|
||||
<div className="edit-profile-page__custom-field" key={key}>
|
||||
<FormField name={customField.name} label={customField.name} infoMessage={customField.description} field="select" fieldProps={{size:'small', items}}/>
|
||||
<FormField name={name} label={name} infoMessage={description} field="select" fieldProps={{size: 'small', items}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -105,7 +148,6 @@ class DashboardEditProfilePage extends React.Component {
|
|||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderMessagePass() {
|
||||
|
@ -121,13 +163,19 @@ class DashboardEditProfilePage extends React.Component {
|
|||
|
||||
onCustomFieldsSubmit(form) {
|
||||
const {customFields} = this.state;
|
||||
const parsedFrom = {}
|
||||
const parsedFrom = {};
|
||||
|
||||
customFields.forEach(customField => {
|
||||
if(customField.type === 'select') {
|
||||
parsedFrom[getCustomFieldParamName(customField.name)] = customField.options[form[customField.name]].name;
|
||||
const {
|
||||
type,
|
||||
name,
|
||||
options
|
||||
} = customField;
|
||||
|
||||
if(type === 'select') {
|
||||
parsedFrom[getCustomFieldParamName(name)] = options[form[name]].name;
|
||||
} else {
|
||||
parsedFrom[getCustomFieldParamName(customField.name)] = form[customField.name];
|
||||
parsedFrom[getCustomFieldParamName(name)] = form[name];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -142,7 +190,6 @@ class DashboardEditProfilePage extends React.Component {
|
|||
this.setState({loadingCustomFields: false});
|
||||
this.props.dispatch(SessionActions.getUserData());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onSubmitEditEmail(formState) {
|
||||
|
@ -157,6 +204,7 @@ class DashboardEditProfilePage extends React.Component {
|
|||
this.setState({
|
||||
loadingEmail: true
|
||||
});
|
||||
|
||||
return API.call({
|
||||
path: "/user/edit-email",
|
||||
data: {
|
||||
|
@ -179,6 +227,7 @@ class DashboardEditProfilePage extends React.Component {
|
|||
this.setState({
|
||||
loadingPass: true
|
||||
});
|
||||
|
||||
return API.call({
|
||||
path: "/user/edit-password",
|
||||
data: {
|
||||
|
@ -206,12 +255,19 @@ class DashboardEditProfilePage extends React.Component {
|
|||
.then(result => {
|
||||
const customFieldsFrom = {};
|
||||
const {userCustomFields} = this.props;
|
||||
|
||||
result.data.forEach(customField => {
|
||||
if(customField.type === 'select') {
|
||||
const index = _.indexOf(customField.options.map(option => option.name), userCustomFields[customField.name]);
|
||||
customFieldsFrom[customField.name] = (index === -1 ? 0 : index);
|
||||
const {
|
||||
type,
|
||||
name,
|
||||
options
|
||||
} = customField;
|
||||
|
||||
if(type === 'select') {
|
||||
const index = _.indexOf(options.map(option => option.name), userCustomFields[name]);
|
||||
customFieldsFrom[name] = ((index === -1) ? 0 : index);
|
||||
} else {
|
||||
customFieldsFrom[customField.name] = userCustomFields[customField.name] || '';
|
||||
customFieldsFrom[name] = userCustomFields[name] || '';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -22,4 +22,13 @@
|
|||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
&__change-email-container,
|
||||
&__change-password-container,
|
||||
&__edit-custom-field-form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class MainCheckTicketPage extends React.Component {
|
|||
<div className="main-check-ticket-page__captcha">
|
||||
<Captcha ref="captcha"/>
|
||||
</div>
|
||||
<SubmitButton>{i18n('CHECK_TICKET')}</SubmitButton>
|
||||
<SubmitButton type="secondary">{i18n('CHECK_TICKET')}</SubmitButton>
|
||||
</Form>
|
||||
</Widget>
|
||||
</div>
|
||||
|
|
|
@ -37,7 +37,7 @@ class Listing extends React.Component {
|
|||
return (
|
||||
<div className="listing__add">
|
||||
<Button type="secondary" size="auto" className="listing__add-button" onClick={this.props.onAddClick}>
|
||||
<Icon name="plus-circle"/> {this.props.addNewText}
|
||||
<Icon name="plus-circle" /> {this.props.addNewText}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -137,6 +137,7 @@ export default {
|
|||
'UPDATE_EMAIL': 'Update email',
|
||||
'UPDATE_PASSWORD': 'Update password',
|
||||
'UPDATE_LEVEL': 'Update level',
|
||||
'UPDATE_CUSTOM_FIELDS': 'Update custom fields',
|
||||
'UPDATE_DEPARTMENTS': 'Update departments',
|
||||
'EDIT_STAFF': 'Edit staff member',
|
||||
'ADD_DEPARTMENT': 'Add department',
|
||||
|
@ -416,6 +417,7 @@ export default {
|
|||
'OLD_PASSWORD_INCORRECT': 'Old password is incorrect',
|
||||
'WILL_LOSE_CHANGES': 'You haven\'t save. Your changes will be lost.',
|
||||
'WILL_DELETE_CUSTOM_RESPONSE': 'The custom response will be deleted.',
|
||||
'WILL_DELETE_CUSTOM_TAG': 'The custom tag will be deleted.',
|
||||
'WILL_DELETE_DEPARTMENT': 'The department will be deleted. All the tickets will be transfer to the department selected.',
|
||||
'NO_STAFF_ASSIGNED': 'No staff member is assigned to this department.',
|
||||
'NO_DEPARTMENT_ASSIGNED': 'No ticket department is assigned you.',
|
||||
|
|
Loading…
Reference in New Issue