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:
LautaroCesso 2020-11-19 17:37:59 -03:00 committed by GitHub
parent 3dd76f214d
commit af3d95cf4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 523 additions and 291 deletions

View File

@ -32,11 +32,11 @@ class ArticleAddModal extends React.Component {
<Form onSubmit={this.onAddNewArticleFormSubmit.bind(this)} loading={this.state.loading}> <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="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}}/> <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) => { <Button className="article-add-modal__cancel-button" type="link" onClick={(event) => {
event.preventDefault(); event.preventDefault();
ModalContainer.closeModal(); ModalContainer.closeModal();
}}>{i18n('CANCEL')}</Button> }}>{i18n('CANCEL')}</Button>
<SubmitButton className="article-add-modal__submit-button" type="secondary">{i18n('ADD_ARTICLE')}</SubmitButton>
</Form> </Form>
</div> </div>
); );

View File

@ -2,7 +2,11 @@
width: 800px; width: 800px;
&__cancel-button { &__cancel-button {
float: right; float: left;
margin-top: 15px; margin-top: 15px;
} }
&__submit-button {
float: right;
}
} }

View File

@ -36,11 +36,13 @@ class ArticlesList extends React.Component {
} }
render() { render() {
if(this.props.errored) { const { errored, loading } = this.props;
if(errored) {
return <Message type="error">{i18n('ERROR_RETRIEVING_ARTICLES')}</Message>; return <Message type="error">{i18n('ERROR_RETRIEVING_ARTICLES')}</Message>;
} }
return (this.props.loading) ? <Loading /> : this.renderContent(); return loading ? <Loading /> : this.renderContent();
} }
renderContent() { renderContent() {
@ -53,17 +55,19 @@ class ArticlesList extends React.Component {
} }
renderTopics() { renderTopics() {
const { topics, editable, articlePath } = this.props;
return ( return (
<div className="articles-list__topics"> <div className="articles-list__topics">
{this.props.topics.map((topic, index) => { {topics.map((topic, index) => {
return ( return (
<div key={index}> <div key={index}>
<TopicViewer <TopicViewer
{...topic} {...topic}
id={topic.id * 1} id={topic.id * 1}
editable={this.props.editable} editable={editable}
onChange={this.retrieveArticles.bind(this)} onChange={this.retrieveArticles.bind(this)}
articlePath={this.props.articlePath} /> articlePath={articlePath} />
<span className="separator" /> <span className="separator" />
</div> </div>
); );
@ -76,7 +80,7 @@ class ArticlesList extends React.Component {
return ( return (
<div className="articles-list__add-topic-button"> <div className="articles-list__add-topic-button">
<Button onClick={() => ModalContainer.openModal(<TopicEditModal addForm onChange={this.retrieveArticles.bind(this)} />)} type="secondary" className="articles-list__add"> <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> </Button>
</div> </div>
); );
@ -88,9 +92,11 @@ class ArticlesList extends React.Component {
} }
export default connect((store) => { export default connect((store) => {
const { topics, errored, loading } = store.articles;
return { return {
topics: store.articles.topics.map((topic) => {return {...topic, private: topic.private === "1"}}), topics: topics.map((topic) => {return {...topic, private: topic.private === "1"}}),
errored: store.articles.errored, errored,
loading: store.articles.loading loading
}; };
})(ArticlesList); })(ArticlesList);

View File

@ -1,14 +1,14 @@
@import "../scss/vars"; @import "../scss/vars";
.articles-list { .articles-list {
&__add { &__add {
position: relative; position: relative;
} }
&__add-icon { &__add-topic-button {
position: absolute; display: flex;
left: 10px; flex-direction: row;
margin-top: -4px; justify-content: flex-end;
align-items: center;
} }
} }

View File

@ -37,15 +37,16 @@ class TopicEditModal extends React.Component {
<FormField name="title" label={i18n('TITLE')} fieldProps={{size: 'large'}} validation="TITLE" required /> <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="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 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')} /> <InfoTooltip className="topic-edit-modal__private" text={i18n('PRIVATE_TOPIC_DESCRIPTION')} />
<div className="topic-edit-modal__buttons-container">
<SubmitButton className="topic-edit-modal__save-button" type="secondary" size="small"> <Button className="topic-edit-modal__discard-button" onClick={this.onDiscardClick.bind(this)} size="small">
{i18n('SAVE')} {i18n('CANCEL')}
</SubmitButton> </Button>
<Button className="topic-edit-modal__discard-button" onClick={this.onDiscardClick.bind(this)} size="small"> <SubmitButton className="topic-edit-modal__save-button" type="secondary" size="small">
{i18n('CANCEL')} {i18n('SAVE')}
</Button> </SubmitButton>
</div>
</Form> </Form>
</div> </div>
); );

View File

@ -13,9 +13,19 @@
} }
&__discard-button { &__buttons-container {
float: right; width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 20px 0 0 0;
} }
&__discard-button {
float: left;
}
&__private { &__private {
display: inline-block; display: inline-block;
padding-right: 10px; padding-right: 10px;

View File

@ -66,12 +66,12 @@ class AdminPanelViewArticle extends React.Component {
return ( return (
<div className="admin-panel-view-article__content"> <div className="admin-panel-view-article__content">
<div className="admin-panel-view-article__edit-buttons"> <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)}> <Button size="medium" onClick={this.onDeleteClick.bind(this, article)}>
{i18n('DELETE')} {i18n('DELETE')}
</Button> </Button>
<Button className="admin-panel-view-article__edit-button" size="medium" onClick={this.onEditClick.bind(this, article)} type="tertiary">
{i18n('EDIT')}
</Button>
</div> </div>
<div className="admin-panel-view-article__article"> <div className="admin-panel-view-article__article">
<Header title={article.title}/> <Header title={article.title}/>
@ -91,10 +91,10 @@ class AdminPanelViewArticle extends React.Component {
return ( return (
<Form values={this.state.form} onChange={(form) => this.setState({form})} onSubmit={this.onFormSubmit.bind(this)}> <Form values={this.state.form} onChange={(form) => this.setState({form})} onSubmit={this.onFormSubmit.bind(this)}>
<div className="admin-panel-view-article__buttons"> <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)}> <Button className="admin-panel-view-article__button" size="medium" onClick={this.onFormCancel.bind(this)}>
{i18n('CANCEL')} {i18n('CANCEL')}
</Button> </Button>
<SubmitButton className="admin-panel-view-article__button" type="secondary" size="medium">{i18n('SAVE')}</SubmitButton>
</div> </div>
<FormField name="title" label={i18n('TITLE')} /> <FormField name="title" label={i18n('TITLE')} />
<FormField name="content" label={i18n('CONTENT')} field="textarea" validation="TEXT_AREA" required fieldProps={{allowImages: this.props.allowAttachments}}/> <FormField name="content" label={i18n('CONTENT')} field="textarea" validation="TEXT_AREA" required fieldProps={{allowImages: this.props.allowAttachments}}/>

View File

@ -5,7 +5,11 @@
} }
&__edit-buttons { &__edit-buttons {
text-align: left; display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 200px;
margin-bottom: 20px; margin-bottom: 20px;
} }

View File

@ -12,7 +12,8 @@ import ColorSelector from 'core-components/color-selector';
class AdminPanelCustomTagsModal extends React.Component { class AdminPanelCustomTagsModal extends React.Component {
static contextTypes = { static contextTypes = {
closeModal: React.PropTypes.func closeModal: React.PropTypes.func,
createTag: React.PropTypes.bool
}; };
static propTypes = { static propTypes = {
@ -27,57 +28,50 @@ class AdminPanelCustomTagsModal extends React.Component {
render() { render() {
return ( return (
this.props.createTag ? this.renderCreateTagContent() : this.renderEditTagContent() this.renderTagContentPopUp(this.props.createTag)
); );
} }
renderEditTagContent() { renderTagContentPopUp(create) {
return ( const {
<div className='admin-panel-custom-tags-modal'> form,
<Header title={i18n('EDIT_CUSTOM_TAG')} description={i18n('DESCRIPTION_EDIT_CUSTOM_TAG')} /> errors,
<Form loading,
values={this.state.form} } = this.state;
onChange={this.onFormChange.bind(this)} let title, description, nameRequired, submitFunction;
onSubmit={this.onSubmitEditTag.bind(this)}
errors={this.state.errors} if(create) {
onValidateErrors={errors => this.setState({errors})} title = i18n('ADD_CUSTOM_TAG');
loading={this.state.loading}> description = i18n('DESCRIPTION_ADD_CUSTOM_TAG');
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}}/> submitFunction = this.onSubmitNewTag.bind(this);
<FormField name="color" label={i18n('COLOR')} decorator={ColorSelector} /> nameRequired = true;
<div className='admin-panel-custom-tags-modal__actions'> } else {
<SubmitButton type="secondary" size="small"> title = i18n('EDIT_CUSTOM_TAG');
{i18n('SAVE')} description = i18n('DESCRIPTION_EDIT_CUSTOM_TAG');
</SubmitButton> nameRequired = false;
<Button onClick={this.onDiscardClick.bind(this)} size="small"> submitFunction = this.onSubmitEditTag.bind(this);
{i18n('CANCEL')} }
</Button>
</div>
</Form>
</div>
);
}
renderCreateTagContent() {
return ( return (
<div className='admin-panel-custom-tags-modal'> <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 <Form
values={this.state.form} values={form}
onChange={this.onFormChange.bind(this)} onChange={this.onFormChange.bind(this)}
onSubmit={this.onSubmitNewTag.bind(this)} onSubmit={submitFunction}
errors={this.state.errors} errors={errors}
onValidateErrors={errors => this.setState({errors})} onValidateErrors={errors => this.setState({errors})}
loading={this.state.loading}> loading={loading}>
<FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} required /> <FormField name="name" label={i18n('NAME')} fieldProps={{size: 'large'}} required={nameRequired} />
<FormField name="color" label={i18n('COLOR')} decorator={ColorSelector} /> <FormField name="color" label={i18n('COLOR')} decorator={ColorSelector} />
<div className='admin-panel-custom-tags-modal__actions'> <div className='admin-panel-custom-tags-modal__actions'>
<SubmitButton type="secondary" size="small"> <Button onClick={this.onDiscardClick.bind(this)} size="small">
{i18n('SAVE')} {i18n('CANCEL')}
</SubmitButton> </Button>
<Button onClick={this.onDiscardClick.bind(this)} size="small"> <SubmitButton type="secondary" size="small">
{i18n('CANCEL')} {i18n('SAVE')}
</Button> </SubmitButton>
</div> </div>
</Form> </Form>
</div> </div>
); );

View File

@ -44,7 +44,7 @@ class AdminPanelCustomTags extends React.Component {
<div className="admin-panel-custom-tags__content"> <div className="admin-panel-custom-tags__content">
<div> <div>
<Button onClick={this.openTagModal.bind(this)} type="secondary"> <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> </Button>
</div> </div>
<div className="admin-panel-custom-tags__tag-list"> <div className="admin-panel-custom-tags__tag-list">
@ -77,13 +77,13 @@ class AdminPanelCustomTags extends React.Component {
openEditTagModal(tagId,tagName,tagColor, event) { openEditTagModal(tagId,tagName,tagColor, event) {
ModalContainer.openModal( 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) { onDeleteClick(tagId, event) {
event.preventDefault(); 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) { deleteCustomTag(tagId) {

View File

@ -138,11 +138,12 @@ class AdminPanelEmailSettings extends React.Component {
<FormField name="smtp-user" label={i18n('SMTP_USER')} fieldProps={{size: 'large'}} /> <FormField name="smtp-user" label={i18n('SMTP_USER')} fieldProps={{size: 'large'}} />
<FormField name="smtp-pass" label={i18n('SMTP_PASSWORD')} fieldProps={{size: 'large', autoComplete: 'off'}} /> <FormField name="smtp-pass" label={i18n('SMTP_PASSWORD')} fieldProps={{size: 'large', autoComplete: 'off'}} />
<div className="admin-panel-email-settings__server-form-buttons"> <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)}> <SubmitButton type="tertiary" size="small" onClick={this.testSMTP.bind(this)}>
{i18n('TEST')} {i18n('TEST')}
</SubmitButton> </SubmitButton>
<SubmitButton className="admin-panel-email-settings__submit" type="secondary" size="small">
{i18n('SAVE')}
</SubmitButton>
</div> </div>
</Form> </Form>
</div> </div>
@ -160,11 +161,12 @@ class AdminPanelEmailSettings extends React.Component {
infoMessage={i18n('IMAP_TOKEN_DESCRIPTION')} infoMessage={i18n('IMAP_TOKEN_DESCRIPTION')}
fieldProps={{size: 'large', icon: 'refresh', onIconClick: this.generateImapToken.bind(this)}} /> fieldProps={{size: 'large', icon: 'refresh', onIconClick: this.generateImapToken.bind(this)}} />
<div className="admin-panel-email-settings__server-form-buttons"> <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)}> <SubmitButton type="tertiary" size="small" onClick={this.testIMAP.bind(this)}>
{i18n('TEST')} {i18n('TEST')}
</SubmitButton> </SubmitButton>
<SubmitButton className="admin-panel-email-settings__submit" type="secondary" size="small">
{i18n('SAVE')}
</SubmitButton>
</div> </div>
</Form> </Form>
<Message className="admin-panel-email-settings__imap-message" type="info"> <Message className="admin-panel-email-settings__imap-message" type="info">
@ -177,10 +179,16 @@ class AdminPanelEmailSettings extends React.Component {
} }
renderForm() { renderForm() {
const {
form,
language,
selectedIndex,
edited
} = this.state;
return ( return (
<div className="col-md-9"> <div className="col-md-9">
<FormField label={i18n('LANGUAGE')} decorator={LanguageSelector} value={this.state.language} <FormField label={i18n('LANGUAGE')} decorator={LanguageSelector} value={language}
onChange={event => this.onItemChange(this.state.selectedIndex, event.target.value)} onChange={event => this.onItemChange(selectedIndex, event.target.value)}
fieldProps={{ fieldProps={{
type: 'allowed', type: 'allowed',
size: 'medium' size: 'medium'
@ -196,29 +204,32 @@ class AdminPanelEmailSettings extends React.Component {
<FormField key="text1" label={i18n('TEXT') + '1'} name="text1" validation="TEXT_AREA" required <FormField key="text1" label={i18n('TEXT') + '1'} name="text1" validation="TEXT_AREA" required
decorator={'textarea'} decorator={'textarea'}
fieldProps={{className: 'admin-panel-email-settings__text-area'}} /> 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 <FormField key="text2" label={i18n('TEXT') + '2'} name="text2" validation="TEXT_AREA" required
decorator={'textarea'} decorator={'textarea'}
fieldProps={{className: 'admin-panel-email-settings__text-area'}} /> : null} 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 <FormField key="text3" label={i18n('TEXT') + '3'} name="text3" validation="TEXT_AREA" required
decorator={'textarea'} decorator={'textarea'}
fieldProps={{className: 'admin-panel-email-settings__text-area'}} /> : null} fieldProps={{className: 'admin-panel-email-settings__text-area'}} /> : null}
<div className="admin-panel-email-settings__actions"> <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"> <div className="admin-panel-email-settings__optional-buttons">
{(this.state.edited) ? this.renderDiscardButton() : null}
<div className="admin-panel-email-settings__recover-button"> <div className="admin-panel-email-settings__recover-button">
<Button onClick={this.onRecoverClick.bind(this)} size="medium"> <Button onClick={this.onRecoverClick.bind(this)} size="medium">
{i18n('RECOVER_DEFAULT')} {i18n('RECOVER_DEFAULT')}
</Button> </Button>
</div> </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>
</div> </div>
</Form> </Form>

View File

@ -17,12 +17,12 @@
&__save-button { &__save-button {
display: inline-block; display: inline-block;
float: left; float: right;
} }
&__optional-buttons { &__optional-buttons {
display: inline-block; display: inline-block;
float: right; float: left;
} }
&__discard-button { &__discard-button {
@ -31,7 +31,7 @@
&__recover-button { &__recover-button {
display: inline-block; display: inline-block;
margin-left: 10px; margin: 0 10px;
} }
&__image-container, &__image-container,

View File

@ -100,10 +100,10 @@ class AdminPanelSystemPreferences extends React.Component {
</div> </div>
<div className="row admin-panel-system-preferences__container"> <div className="row admin-panel-system-preferences__container">
<div className="col-md-4 col-md-offset-2"> <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>
<div className="col-md-4"> <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>
</div> </div>
</Form> </Form>

View File

@ -14,7 +14,6 @@ import ModalContainer from 'app-components/modal-container';
import InviteStaffModal from 'app/admin/panel/staff/invite-staff-modal'; import InviteStaffModal from 'app/admin/panel/staff/invite-staff-modal';
import Header from 'core-components/header'; import Header from 'core-components/header';
import DropDown from 'core-components/drop-down';
import Button from 'core-components/button'; import Button from 'core-components/button';
import Icon from 'core-components/icon'; import Icon from 'core-components/icon';
import Loading from 'core-components/loading'; import Loading from 'core-components/loading';
@ -48,7 +47,7 @@ class AdminPanelStaffMembers extends React.Component {
<div className="admin-panel-staff-members__wrapper"> <div className="admin-panel-staff-members__wrapper">
<DepartmentDropdown {...this.getDepartmentDropdownProps()} className="admin-panel-staff-members__dropdown" /> <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"> <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> </Button>
</div> </div>
{(this.props.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />} {(this.props.loading) ? <Loading backgrounded /> : <PeopleList list={this.getStaffList()} page={this.state.page} onPageSelect={(index) => this.setState({page: index+1})} />}

View File

@ -61,7 +61,7 @@ class AdminPanelCustomResponses extends React.Component {
return ( return (
<div className="row"> <div className="row">
<div className="col-md-3"> <div className="col-md-3">
<Listing {...this.getListingProps()}/> <Listing {...this.getListingProps()} />
</div> </div>
{this.state.showForm ? this.renderForm() : null} {this.state.showForm ? this.renderForm() : null}
</div> </div>
@ -71,7 +71,7 @@ class AdminPanelCustomResponses extends React.Component {
renderLoading() { renderLoading() {
return ( return (
<div className="admin-panel-custom-responses__loading"> <div className="admin-panel-custom-responses__loading">
<Loading backgrounded size="large"/> <Loading backgrounded size="large" />
</div> </div>
); );
} }
@ -82,7 +82,7 @@ class AdminPanelCustomResponses extends React.Component {
<Form {...this.getFormProps()}> <Form {...this.getFormProps()}>
<div className="row"> <div className="row">
<div className="col-md-7"> <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>
<div className="col-md-5"> <div className="col-md-5">
<FormField label={i18n('LANGUAGE')} name="language" field="input" decorator={LanguageSelector} fieldProps={{size: 'medium'}} /> <FormField label={i18n('LANGUAGE')} name="language" field="input" decorator={LanguageSelector} fieldProps={{size: 'medium'}} />
@ -90,10 +90,10 @@ class AdminPanelCustomResponses extends React.Component {
</div> </div>
<FormField label={i18n('CONTENT')} name="content" validation="TEXT_AREA" required field="textarea" /> <FormField label={i18n('CONTENT')} name="content" validation="TEXT_AREA" required field="textarea" />
<div className="admin-panel-custom-responses__actions"> <div className="admin-panel-custom-responses__actions">
{(this.state.selectedIndex !== -1) ? this.renderOptionalButtons() : null}
<div className="admin-panel-custom-responses__save-button"> <div className="admin-panel-custom-responses__save-button">
<SubmitButton type="secondary" size="small">{i18n('SAVE')}</SubmitButton> <SubmitButton type="secondary" size="small">{i18n('SAVE')}</SubmitButton>
</div> </div>
{(this.state.selectedIndex !== -1) ? this.renderOptionalButtons() : null}
</div> </div>
</Form> </Form>
</div> </div>
@ -103,12 +103,12 @@ class AdminPanelCustomResponses extends React.Component {
renderOptionalButtons() { renderOptionalButtons() {
return ( return (
<div className="admin-panel-custom-responses__optional-buttons"> <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"> <div className="admin-panel-custom-responses__delete-button">
<Button onClick={this.onDeleteClick.bind(this)}>{i18n('DELETE')}</Button> <Button onClick={this.onDeleteClick.bind(this)}>{i18n('DELETE')}</Button>
</div> </div>
<div className="admin-panel-custom-responses__discard-button">
{this.isEdited() ? <Button onClick={this.onDiscardChangesClick.bind(this)}>{i18n('DISCARD_CHANGES')}</Button> : null}
</div>
</div> </div>
); );
} }
@ -125,10 +125,16 @@ class AdminPanelCustomResponses extends React.Component {
} }
getFormProps() { getFormProps() {
const {
form,
errors,
formLoading
} = this.state;
return { return {
values: this.state.form, values: form,
errors: this.state.errors, errors,
loading: this.state.formLoading, loading: formLoading,
onClick: () => this.setState({formClicked: true}), onClick: () => this.setState({formClicked: true}),
onChange: (form) => this.setState({form}), onChange: (form) => this.setState({form}),
onValidateErrors: (errors) => {this.setState({errors})}, onValidateErrors: (errors) => {this.setState({errors})},
@ -143,7 +149,7 @@ class AdminPanelCustomResponses extends React.Component {
<span> <span>
{item.name} {item.name}
<span className="admin-panel-custom-responses__item-flag"> <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>
</span> </span>
) )
@ -161,13 +167,15 @@ class AdminPanelCustomResponses extends React.Component {
onFormSubmit(form) { onFormSubmit(form) {
const {items, allowedLanguages} = this.props; const {items, allowedLanguages} = this.props;
const { selectedIndex } = this.state;
this.setState({formLoading: true}); this.setState({formLoading: true});
if(this.state.selectedIndex !== -1) { if(selectedIndex !== -1) {
API.call({ API.call({
path: '/ticket/edit-custom-response', path: '/ticket/edit-custom-response',
data: { data: {
id: items[this.state.selectedIndex].id, id: items[selectedIndex].id,
name: form.title, name: form.title,
content: form.content, content: form.content,
language: _.includes(allowedLanguages, form.language) ? form.language : allowedLanguages[0] language: _.includes(allowedLanguages, form.language) ? form.language : allowedLanguages[0]
@ -215,11 +223,17 @@ class AdminPanelCustomResponses extends React.Component {
} }
updateForm(index) { updateForm(index) {
const {
items,
language
} = this.props;
const item = items[index];
let form = _.clone(this.state.form); let form = _.clone(this.state.form);
form.title = (this.props.items[index] && this.props.items[index].name) || ''; form.title = (item && item.name) || '';
form.content = TextEditor.getEditorStateFromHTML((this.props.items[index] && this.props.items[index].content) || ''); form.content = TextEditor.getEditorStateFromHTML((item && item.content) || '');
form.language = (this.props.items[index] && this.props.items[index].language) || this.props.language; form.language = (item && item.language) || language;
this.setState({ this.setState({
formClicked: false, formClicked: false,
@ -227,7 +241,7 @@ class AdminPanelCustomResponses extends React.Component {
selectedIndex: index, selectedIndex: index,
formLoading: false, formLoading: false,
originalForm: form, originalForm: form,
form: form, form,
errors: {} errors: {}
}); });
} }
@ -237,10 +251,18 @@ class AdminPanelCustomResponses extends React.Component {
} }
isEdited() { isEdited() {
return this.state.form.title && this.state.formClicked && ( const {
this.state.form.title != this.state.originalForm.title || form,
this.state.form.content != this.state.originalForm.content || formClicked,
this.state.form.language != this.state.originalForm.language originalForm
} = this.state;
return (
form.title && formClicked && (
form.title != originalForm.title ||
form.content != originalForm.content ||
form.language != originalForm.language
)
); );
} }
} }

View File

@ -7,25 +7,25 @@
float: right; float: right;
} }
&__actions { &__actions,
text-align: left; &__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 { &__save-button {
display: inline-block; display: flex;
margin-right: 30px; flex-direction: row;
} justify-content: flex-end;
align-items: center;
&__optional-buttons { width: 100%;
display: inline;
}
&__discard-button {
display: inline-block;
}
&__delete-button {
display: inline-block;
float: right;
} }
} }

View File

@ -36,10 +36,10 @@ class AdminPanelMyTickets extends React.Component {
return ( return (
<div className="admin-panel-my-tickets"> <div className="admin-panel-my-tickets">
<Header title={i18n('MY_TICKETS')} description={i18n('MY_TICKETS_DESCRIPTION')} /> <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}}> <div style={{textAlign: 'right', marginTop: 10}}>
<Button onClick={this.onCreateTicket.bind(this)} type="secondary" size="medium"> <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> </Button>
</div> </div>
</div> </div>
@ -47,21 +47,31 @@ class AdminPanelMyTickets extends React.Component {
} }
getProps() { getProps() {
const { closedTicketsShown } = this.state;
const {
userId,
departments,
tickets,
loading,
pages,
page
} = this.props;
return { return {
userId: this.props.userId, userId,
departments: this.props.departments, departments ,
tickets: this.props.tickets, tickets,
type: 'secondary', type: 'secondary',
loading: this.props.loading, loading,
ticketPath: '/admin/panel/tickets/view-ticket/', ticketPath: '/admin/panel/tickets/view-ticket/',
closedTicketsShown: this.state.closedTicketsShown, closedTicketsShown,
onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this), onClosedTicketsShownChange: this.onClosedTicketsShownChange.bind(this),
pages: this.props.pages, pages,
page: this.props.page, page,
onPageChange: event => this.retrieveMyTickets(event.target.value), onPageChange: event => this.retrieveMyTickets(event.target.value),
onDepartmentChange: departmentId => { onDepartmentChange: departmentId => {
this.setState({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() { onCreateTicket() {
ModalContainer.openModal( ModalContainer.openModal(
<div> <div className="admin-panel-my-tickets__create-ticket-form-container">
<CreateTicketForm isStaff={true} onSuccess={this.onCreateTicketSuccess.bind(this)} /> <CreateTicketForm
<div style={{textAlign: 'center'}}> className="admin-panel-my-tickets__create-ticket-form"
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button> 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>
</div> </div>
); );

View File

@ -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%;
}
}

View File

@ -26,24 +26,30 @@ class AdminPanelCustomFieldForm extends React.Component {
}; };
render() { render() {
const {
loading,
addForm,
error
} = this.state;
return ( return (
<div className="admin-panel-custom-field-form"> <div className="admin-panel-custom-field-form">
<Header title={i18n('NEW_CUSTOM_FIELD')} description={i18n('NEW_CUSTOM_FIELD_DESCRIPTION')} /> <Header title={i18n('NEW_CUSTOM_FIELD')} description={i18n('NEW_CUSTOM_FIELD_DESCRIPTION')} />
<div className="admin-panel-custom-field-form__form-container"> <div className="admin-panel-custom-field-form__form-container">
<Form <Form
className="admin-panel-custom-field-form__form" className="admin-panel-custom-field-form__form"
loading={this.state.loading} loading={loading}
values={this.state.addForm} values={addForm}
onChange={this.onAddFormChange.bind(this)} onChange={this.onAddFormChange.bind(this)}
onSubmit={this.onSubmit.bind(this)}> onSubmit={this.onSubmit.bind(this)}>
<FormField name="name" validation="NAME" label={i18n('NAME')} field="input" fieldProps={{size: 'large'}} required/> <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="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/> <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} {addForm.type ? this.renderOptionFormFields() : null}
{this.state.error ? this.renderErrorMessage() : null} {error ? this.renderErrorMessage() : null}
<div className="admin-panel-custom-field-form__buttons"> <div className="admin-panel-custom-field-form__buttons">
<SubmitButton>{i18n('SUBMIT')}</SubmitButton>
<Button onClick={this.props.onClose} type="link">{i18n('CLOSE')}</Button> <Button onClick={this.props.onClose} type="link">{i18n('CLOSE')}</Button>
<SubmitButton type="secondary">{i18n('SUBMIT')}</SubmitButton>
</div> </div>
</Form> </Form>
</div> </div>

View File

@ -29,9 +29,9 @@ class AdminPanelCustomFields extends React.Component {
<div className="admin-panel-custom-fields"> <div className="admin-panel-custom-fields">
<Header title={i18n('CUSTOM_FIELDS')} description={i18n('CUSTOM_FIELDS_DESCRIPTION')} /> <Header title={i18n('CUSTOM_FIELDS')} description={i18n('CUSTOM_FIELDS_DESCRIPTION')} />
{this.renderCustomFieldList()} {this.renderCustomFieldList()}
<div className="admin-panel-custom-fields__add-button"> <div className="admin-panel-custom-fields__container-button">
<Button type="secondary" onClick={this.onNewCustomFieldClick.bind(this)}> <Button className="admin-panel-custom-fields__container-button__add-button" type="secondary" onClick={this.onNewCustomFieldClick.bind(this)}>
<Icon name="plus"/> {i18n('NEW_CUSTOM_FIELD')} <Icon name="plus" /> {i18n('NEW_CUSTOM_FIELD')}
</Button> </Button>
</div> </div>
</div> </div>
@ -48,8 +48,7 @@ class AdminPanelCustomFields extends React.Component {
{key: 'options', value: i18n('OPTIONS')}, {key: 'options', value: i18n('OPTIONS')},
{key: 'actions', value: ''}, {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() { onNewCustomFieldClick() {
ModalContainer.openModal( ModalContainer.openModal(
<AdminPanelCustomFieldForm <AdminPanelCustomFieldForm
onClose={ModalContainer.closeModal} onClose={(e) => {e.preventDefault(); ModalContainer.closeModal();}}
onChange={() => { onChange={() => {
this.retrieveCustomFields(); this.retrieveCustomFields();
ModalContainer.closeModal(); ModalContainer.closeModal();
}}/> }} />
); );
} }

View File

@ -4,8 +4,14 @@
text-align: left; text-align: left;
} }
&__add-button { &__container-button {
text-align: left; display: flex;
margin-top: 14px; flex-direction: row;
justify-content: flex-end;
align-items: center;
margin-top: 20px;
&__add-button {
}
} }
} }

View File

@ -48,10 +48,10 @@ class AdminPanelListUsers extends React.Component {
return ( return (
<div> <div>
<SearchBox className="admin-panel-list-users__search-box" placeholder={i18n('SEARCH_USERS')} onSearch={this.onSearch.bind(this)} /> <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}}> <div style={{textAlign: 'right', marginTop: 10}}>
<Button onClick={this.onInviteUser.bind(this)} type="secondary" size="medium"> <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> </Button>
</div> </div>
</div> </div>
@ -138,11 +138,17 @@ class AdminPanelListUsers extends React.Component {
} }
onPageChange(event) { onPageChange(event) {
const {
orderBy,
desc,
search
} = this.state;
this.retrieveUsers({ this.retrieveUsers({
page: event.target.value, page: event.target.value,
orderBy: this.state.orderBy, orderBy,
desc: this.state.desc, desc,
search: this.state.search search
}); });
} }
@ -179,9 +185,6 @@ class AdminPanelListUsers extends React.Component {
ModalContainer.openModal( ModalContainer.openModal(
<div className="admin-panel-list-users__invite-user-form"> <div className="admin-panel-list-users__invite-user-form">
<InviteUserWidget onSuccess={this.onInviteUserSuccess.bind(this)} /> <InviteUserWidget onSuccess={this.onInviteUserSuccess.bind(this)} />
<div style={{textAlign: 'center'}}>
<Button onClick={ModalContainer.closeModal} type="link">{i18n('CLOSE')}</Button>
</div>
</div> </div>
); );
} }

View File

@ -51,31 +51,40 @@ class AdminPanelViewUser extends React.Component {
} }
renderUserInfo() { renderUserInfo() {
const {
name,
disabled,
email,
verified,
customfields,
loading
} = this.state;
return ( return (
<div className="admin-panel-view-user__content"> <div className="admin-panel-view-user__content">
<div className="admin-panel-view-user__info"> <div className="admin-panel-view-user__info">
<div className="admin-panel-view-user__info-item"> <div className="admin-panel-view-user__info-item">
{i18n('NAME')} {i18n('NAME')}
<div className="admin-panel-view-user__info-box"> <div className="admin-panel-view-user__info-box">
{this.state.name} {name}
{(this.state.disabled) ? this.renderDisabled() : null} {disabled ? this.renderDisabled() : null}
</div> </div>
</div> </div>
<div className="admin-panel-view-user__info-item"> <div className="admin-panel-view-user__info-item">
{i18n('EMAIL')} {i18n('EMAIL')}
<div className="admin-panel-view-user__info-box"> <div className="admin-panel-view-user__info-box">
{this.state.email} {email}
{(!this.state.verified) ? this.renderNotVerified() : null} {(!verified) ? this.renderNotVerified() : null}
</div> </div>
</div> </div>
{this.state.customfields.map(this.renderCustomField.bind(this))} {customfields.map(this.renderCustomField.bind(this))}
<div className="admin-panel-view-user__action-buttons"> <div className="admin-panel-view-user__action-buttons">
<Button <Button
className="admin-panel-view-user__action-button" className="admin-panel-view-user__action-button"
onClick={this.onDisableClick.bind(this)} onClick={this.onDisableClick.bind(this)}
size="medium" size="medium"
type={this.state.disabled ? 'tertiary' : 'primary'}> type={disabled ? 'tertiary' : 'primary'}>
{i18n(this.state.disabled ? 'ENABLE_USER' : 'DISABLE_USER')} {i18n(disabled ? 'ENABLE_USER' : 'DISABLE_USER')}
</Button> </Button>
<Button className="admin-panel-view-user__action-button" onClick={this.onDeleteClick.bind(this)} size="medium"> <Button className="admin-panel-view-user__action-button" onClick={this.onDeleteClick.bind(this)} size="medium">
{i18n('DELETE_AND_BAN')} {i18n('DELETE_AND_BAN')}
@ -89,15 +98,14 @@ class AdminPanelViewUser extends React.Component {
<Autocomplete <Autocomplete
onChange={this.onChangeValues.bind(this)} onChange={this.onChangeValues.bind(this)}
getItemListFromQuery={this.searchUsers.bind(this)} getItemListFromQuery={this.searchUsers.bind(this)}
values={this.transformUserListToAutocomplete()} values={this.transformUserListToAutocomplete()} />
/>
<Button <Button
disabled = {this.state.loading} disabled={loading}
type="secondary"
className="admin-panel-view-user__submit-button" className="admin-panel-view-user__submit-button"
onClick={this.onClickSupervisorUserButton.bind(this)} onClick={this.onClickSupervisorUserButton.bind(this)}
size="medium" size="medium">
> {i18n('UPDATE')}
{i18n('UPDATE')}
</Button> </Button>
</div> </div>
{this.renderSupervisedUserMessage()} {this.renderSupervisedUserMessage()}
@ -105,26 +113,28 @@ class AdminPanelViewUser extends React.Component {
<span className="separator" /> <span className="separator" />
<div className="admin-panel-view-user__tickets"> <div className="admin-panel-view-user__tickets">
<div className="admin-panel-view-user__tickets-title">{i18n('TICKETS')}</div> <div className="admin-panel-view-user__tickets-title">{i18n('TICKETS')}</div>
<TicketList {...this.getTicketListProps()}/> <TicketList {...this.getTicketListProps()} />
</div> </div>
</div> </div>
); );
} }
renderSupervisedUserMessage(){ renderSupervisedUserMessage(){
if(this.state.message) { const { message } = this.state;
if(this.state.message != 'success'){
return( if(message) {
if(message != 'success') {
return (
<div className="admin-panel-view-user__supervised-users-message"> <div className="admin-panel-view-user__supervised-users-message">
<Message type="error">{i18n(this.state.message)}</Message> <Message type="error">{i18n(message)}</Message>
</div> </div>
) );
}else{ } else {
return( return (
<div className= "admin-panel-view-user__supervised-users-message"> <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> </div>
) );
} }
} }
} }
@ -177,11 +187,16 @@ class AdminPanelViewUser extends React.Component {
const authorsListWithoutMe = r.data.authors.filter(author => author.id != this.props.params.userId); const authorsListWithoutMe = r.data.authors.filter(author => author.id != this.props.params.userId);
return authorsListWithoutMe.map(author => { return authorsListWithoutMe.map(author => {
const {
id,
name
} = author;
return { return {
name: author.name, name,
color: "gray", color: "gray",
id: author.id*1, id: id*1,
content: <div>{author.name}</div>, content: <div>{name}</div>,
isStaff: false isStaff: false
}}); }});
}).catch((r) => { }).catch((r) => {
@ -192,10 +207,15 @@ class AdminPanelViewUser extends React.Component {
transformUserListToAutocomplete() { transformUserListToAutocomplete() {
return( return(
this.state.userList.map((user) => { this.state.userList.map((user) => {
const {
id,
name
} = user;
return ({ return ({
id: user.id*1, id: id*1,
name: user.name, name,
content: <div>{user.name}</div>, content: <div>{name}</div>,
color: 'grey', color: 'grey',
isStaff: false isStaff: false
}); });
@ -215,37 +235,58 @@ class AdminPanelViewUser extends React.Component {
); );
} }
renderCustomField(customfield) { renderCustomField(_customfield) {
const {
customfield,
value,
id
} = _customfield;
return ( return (
<div className="admin-panel-view-user__info-item"> <div className="admin-panel-view-user__info-item" key={`customFieldId__${id}`}>
{customfield.customfield} {customfield}
<div className="admin-panel-view-user__info-box"> <div className="admin-panel-view-user__info-box">
{customfield.value} {value}
</div> </div>
</div> </div>
); );
} }
getTicketListProps() { getTicketListProps() {
const {
tickets,
loading
} = this.state;
return { return {
type: 'secondary', type: 'secondary',
tickets: this.state.tickets, tickets,
loading: this.state.loading, loading,
departments: this.props.departments, departments: this.props.departments,
ticketPath: '/admin/panel/tickets/view-ticket/' ticketPath: '/admin/panel/tickets/view-ticket/'
}; };
} }
onUserRetrieved(result) { onUserRetrieved(result) {
const {
name,
email,
verified,
tickets,
disabled,
customfields,
userList
} = result.data;
this.setState({ this.setState({
name: result.data.name, name,
email: result.data.email, email,
verified: result.data.verified, verified,
tickets: result.data.tickets, tickets,
disabled: result.data.disabled, disabled,
customfields: result.data.customfields, customfields,
loading: false, loading: false,
userList: result.data.userList userList
}); });
} }

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash'; import _ from 'lodash';
import classNames from 'classnames'; import classNames from 'classnames';
@ -13,6 +12,8 @@ import Form from 'core-components/form';
import FormField from 'core-components/form-field'; import FormField from 'core-components/form-field';
import Widget from 'core-components/widget'; import Widget from 'core-components/widget';
import Header from 'core-components/header'; import Header from 'core-components/header';
import Button from 'core-components/button';
import ModalContainer from 'app-components/modal-container';
class InviteUserWidget extends React.Component { class InviteUserWidget extends React.Component {
@ -52,9 +53,11 @@ class InviteUserWidget extends React.Component {
<div className="invite-user-widget__captcha"> <div className="invite-user-widget__captcha">
<Captcha ref="captcha"/> <Captcha ref="captcha"/>
</div> </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> </Form>
{this.renderMessage()} {this.renderMessage()}
</Widget> </Widget>
); );
@ -88,9 +91,9 @@ class InviteUserWidget extends React.Component {
renderMessage() { renderMessage() {
switch (this.state.message) { switch (this.state.message) {
case 'success': 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': 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: default:
return null; return null;
} }

View File

@ -1,9 +1,8 @@
.invite-user-widget { .invite-user-widget {
padding: 30px; padding: 10px;
text-align: center; text-align: center;
&__form { &__form {
margin-bottom: 20px;
} }
&__inputs { &__inputs {
@ -16,4 +15,16 @@
height: 78px; height: 78px;
width: 304px; width: 304px;
} }
&__buttons-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
&__success-message,
&__error-message {
margin-top: 20px;
}
} }

View File

@ -2,7 +2,6 @@ import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import history from 'lib-app/history';
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';
import SessionStore from 'lib-app/session-store'; import SessionStore from 'lib-app/session-store';
@ -45,36 +44,46 @@ class CreateTicketForm extends React.Component {
}; };
render() { render() {
const {
userLogged,
isDefaultDepartmentLocked,
isStaff,
onlyOneSupportedLanguage,
allowAttachments
} = this.props;
return ( return (
<div className="create-ticket-form"> <div className="create-ticket-form">
<Header title={i18n('CREATE_TICKET')} description={i18n('CREATE_TICKET_DESCRIPTION')} /> <Header title={i18n('CREATE_TICKET')} description={i18n('CREATE_TICKET_DESCRIPTION')} />
<Form {...this.getFormProps()}> <Form {...this.getFormProps()}>
{(!this.props.userLogged) ? this.renderEmailAndName() : null} {(!userLogged) ? this.renderEmailAndName() : null}
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}}/> <FormField label={i18n('TITLE')} name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}} />
<div className="row"> <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={{ <FormField className="col-md-5" label={i18n('DEPARTMENT')} name="departmentIndex" field="select" decorator={DepartmentDropdown} fieldProps={{
departments: SessionStore.getDepartments(), departments: SessionStore.getDepartments(),
size: 'medium' size: 'medium'
}} /> : null }} /> : null
} }
{!this.props.onlyOneSupportedLanguage ? {!onlyOneSupportedLanguage ?
<FormField className="col-md-5" label={i18n('LANGUAGE')} name="language" field="select" decorator={LanguageSelector} fieldProps={{ <FormField className="col-md-5" label={i18n('LANGUAGE')} name="language" field="select" decorator={LanguageSelector} fieldProps={{
type: 'supported', type: 'supported',
size: 'medium' size: 'medium'
}}/> : null }} /> : null
} }
</div> </div>
<FormField <FormField
label={i18n('CONTENT')} label={i18n('CONTENT')}
name="content" name="content"
validation="TEXT_AREA" validation="TEXT_AREA"
fieldProps={{allowImages: this.props.allowAttachments}} fieldProps={{allowImages: allowAttachments}}
required required
field="textarea" /> field="textarea" />
{(this.props.allowAttachments) ? this.renderFileUpload() : null} <div className="create-ticket-form__buttons-container">
{(!this.props.userLogged) ? this.renderCaptcha() : null} {allowAttachments ? this.renderFileUpload() : null}
<SubmitButton>{i18n('CREATE_TICKET')}</SubmitButton> {(!userLogged) ? this.renderCaptcha() : null}
<SubmitButton type="secondary">{i18n('CREATE_TICKET')}</SubmitButton>
</div>
</Form> </Form>
{this.renderMessage()} {this.renderMessage()}
</div> </div>
@ -84,8 +93,8 @@ class CreateTicketForm extends React.Component {
renderEmailAndName() { renderEmailAndName() {
return ( return (
<div className="row"> <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('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('FULL_NAME')} name="name" validation="NAME" required field="input" fieldProps={{size: 'large'}} />
</div> </div>
); );
} }
@ -101,7 +110,7 @@ class CreateTicketForm extends React.Component {
renderCaptcha() { renderCaptcha() {
return ( return (
<div className="create-ticket-form__captcha"> <div className="create-ticket-form__captcha">
<Captcha ref="captcha"/> <Captcha ref="captcha" />
</div> </div>
); );
} }
@ -118,10 +127,15 @@ class CreateTicketForm extends React.Component {
} }
getFormProps() { getFormProps() {
const {
loading,
form
} = this.state;
return { return {
loading: this.state.loading, loading,
onSubmit: this.onSubmit.bind(this), onSubmit: this.onSubmit.bind(this),
values: this.state.form, values: form,
onChange: form => this.setState({form}) onChange: form => this.setState({form})
}; };
} }
@ -148,15 +162,16 @@ class CreateTicketForm extends React.Component {
} }
onTicketSuccess(email, result) { onTicketSuccess(email, result) {
let message = 'success' const { onSuccess } = this.props;
this.setState({ const message = 'success';
loading: false,
message: message this.setState(
}, () => { {
if(this.props.onSuccess) { loading: false,
this.props.onSuccess(result, email, message); message
} },
}); () => {onSuccess && onSuccess(result, email, message);}
);
} }
onTicketFail() { onTicketFail() {

View File

@ -8,6 +8,13 @@
margin-top: 20px; margin-top: 20px;
} }
&__buttons-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
&__captcha { &__captcha {
margin: 0 auto 20px; margin: 0 auto 20px;
height: 78px; height: 78px;

View File

@ -43,54 +43,97 @@ class DashboardEditProfilePage extends React.Component {
return ( return (
<div className="edit-profile-page"> <div className="edit-profile-page">
<Header title={i18n('EDIT_PROFILE')} description={i18n('EDIT_PROFILE_VIEW_DESCRIPTION')} /> <Header title={i18n('EDIT_PROFILE')} description={i18n('EDIT_PROFILE_VIEW_DESCRIPTION')} />
<div className="edit-profile-page__title">{i18n('EDIT_EMAIL')}</div> {this.renderEditEmail()}
<Form loading={this.state.loadingEmail} onSubmit={this.onSubmitEditEmail.bind(this)}> {this.renderEditPassword()}
<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.state.customFields.length ? this.renderCustomFields() : null} {this.state.customFields.length ? this.renderCustomFields() : null}
</div> </div>
); );
} }
renderCustomFields() { renderEditEmail() {
return ( 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> <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)}> <Form
<div className="edit-profile-page__custom-fields"> className="edit-profile-page__edit-custom-field-form"
{this.state.customFields.map(this.renderCustomField.bind(this))} loading={loadingCustomFields}
</div> values={customFieldsFrom}
<div className="row"> onChange={form => this.setState({customFieldsFrom: form})}
<SubmitButton>{i18n('SAVE')}</SubmitButton> onSubmit={this.onCustomFieldsSubmit.bind(this)}>
</div> <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> </Form>
</div> </div>
); );
} }
renderCustomField(customField, key) { renderCustomField(customField, key) {
if(customField.type === 'text') { const {
type,
name,
description,
options
} = customField;
if(type === 'text') {
return ( return (
<div className="edit-profile-page__custom-field" key={key}> <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> </div>
); );
} else { } 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 ( return (
<div className="edit-profile-page__custom-field" key={key}> <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> </div>
); );
} }
@ -105,7 +148,6 @@ class DashboardEditProfilePage extends React.Component {
default: default:
return null; return null;
} }
} }
renderMessagePass() { renderMessagePass() {
@ -121,13 +163,19 @@ class DashboardEditProfilePage extends React.Component {
onCustomFieldsSubmit(form) { onCustomFieldsSubmit(form) {
const {customFields} = this.state; const {customFields} = this.state;
const parsedFrom = {} const parsedFrom = {};
customFields.forEach(customField => { customFields.forEach(customField => {
if(customField.type === 'select') { const {
parsedFrom[getCustomFieldParamName(customField.name)] = customField.options[form[customField.name]].name; type,
name,
options
} = customField;
if(type === 'select') {
parsedFrom[getCustomFieldParamName(name)] = options[form[name]].name;
} else { } 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.setState({loadingCustomFields: false});
this.props.dispatch(SessionActions.getUserData()); this.props.dispatch(SessionActions.getUserData());
}); });
} }
onSubmitEditEmail(formState) { onSubmitEditEmail(formState) {
@ -157,6 +204,7 @@ class DashboardEditProfilePage extends React.Component {
this.setState({ this.setState({
loadingEmail: true loadingEmail: true
}); });
return API.call({ return API.call({
path: "/user/edit-email", path: "/user/edit-email",
data: { data: {
@ -179,6 +227,7 @@ class DashboardEditProfilePage extends React.Component {
this.setState({ this.setState({
loadingPass: true loadingPass: true
}); });
return API.call({ return API.call({
path: "/user/edit-password", path: "/user/edit-password",
data: { data: {
@ -206,12 +255,19 @@ class DashboardEditProfilePage extends React.Component {
.then(result => { .then(result => {
const customFieldsFrom = {}; const customFieldsFrom = {};
const {userCustomFields} = this.props; const {userCustomFields} = this.props;
result.data.forEach(customField => { result.data.forEach(customField => {
if(customField.type === 'select') { const {
const index = _.indexOf(customField.options.map(option => option.name), userCustomFields[customField.name]); type,
customFieldsFrom[customField.name] = (index === -1 ? 0 : index); name,
options
} = customField;
if(type === 'select') {
const index = _.indexOf(options.map(option => option.name), userCustomFields[name]);
customFieldsFrom[name] = ((index === -1) ? 0 : index);
} else { } else {
customFieldsFrom[customField.name] = userCustomFields[customField.name] || ''; customFieldsFrom[name] = userCustomFields[name] || '';
} }
}); });

View File

@ -22,4 +22,13 @@
display: inline-block; display: inline-block;
margin-right: 20px; 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;
}
} }

View File

@ -42,7 +42,7 @@ class MainCheckTicketPage extends React.Component {
<div className="main-check-ticket-page__captcha"> <div className="main-check-ticket-page__captcha">
<Captcha ref="captcha"/> <Captcha ref="captcha"/>
</div> </div>
<SubmitButton>{i18n('CHECK_TICKET')}</SubmitButton> <SubmitButton type="secondary">{i18n('CHECK_TICKET')}</SubmitButton>
</Form> </Form>
</Widget> </Widget>
</div> </div>

View File

@ -37,7 +37,7 @@ class Listing extends React.Component {
return ( return (
<div className="listing__add"> <div className="listing__add">
<Button type="secondary" size="auto" className="listing__add-button" onClick={this.props.onAddClick}> <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> </Button>
</div> </div>
); );

View File

@ -137,6 +137,7 @@ export default {
'UPDATE_EMAIL': 'Update email', 'UPDATE_EMAIL': 'Update email',
'UPDATE_PASSWORD': 'Update password', 'UPDATE_PASSWORD': 'Update password',
'UPDATE_LEVEL': 'Update level', 'UPDATE_LEVEL': 'Update level',
'UPDATE_CUSTOM_FIELDS': 'Update custom fields',
'UPDATE_DEPARTMENTS': 'Update departments', 'UPDATE_DEPARTMENTS': 'Update departments',
'EDIT_STAFF': 'Edit staff member', 'EDIT_STAFF': 'Edit staff member',
'ADD_DEPARTMENT': 'Add department', 'ADD_DEPARTMENT': 'Add department',
@ -416,6 +417,7 @@ export default {
'OLD_PASSWORD_INCORRECT': 'Old password is incorrect', 'OLD_PASSWORD_INCORRECT': 'Old password is incorrect',
'WILL_LOSE_CHANGES': 'You haven\'t save. Your changes will be lost.', '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_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.', '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_STAFF_ASSIGNED': 'No staff member is assigned to this department.',
'NO_DEPARTMENT_ASSIGNED': 'No ticket department is assigned you.', 'NO_DEPARTMENT_ASSIGNED': 'No ticket department is assigned you.',