Merge branch 'master' of https://github.com/ONLYOFFICE/CommunityServer-AspNetCore
This commit is contained in:
commit
92a5d6d483
@ -30,10 +30,6 @@ class Profile extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.resetProfile();
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("Profile render")
|
||||
|
||||
|
@ -1,394 +0,0 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import { withRouter } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { Field, FieldArray, reduxForm, formValueSelector } from 'redux-form'
|
||||
import { device, Avatar, Button, TextInput, Textarea, DateInput, Label, RadioButton, Text, toastr, SelectedItem } from 'asc-web-components'
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createProfile, updateProfile } from '../../../../../../store/profile/actions';
|
||||
|
||||
const formName = "userForm";
|
||||
|
||||
const getUserRole = user => {
|
||||
if(user.isOwner) return "owner";
|
||||
if(user.isAdmin) return "admin";
|
||||
if(user.isVisitor) return "guest";
|
||||
return "user";
|
||||
};
|
||||
|
||||
const onEditAvatar = () => {};
|
||||
|
||||
const MainContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@media ${device.tablet} {
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
const AvatarContainer = styled.div`
|
||||
margin: 0 32px 32px 0;
|
||||
width: 160px;
|
||||
`;
|
||||
|
||||
const MainFieldsContainer = styled.div`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const FieldContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0 0 16px 0;
|
||||
|
||||
.field-label {
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
line-height: 32px;
|
||||
display: flex;
|
||||
|
||||
label:not(:first-child) {
|
||||
margin-left: 33px;
|
||||
}
|
||||
}
|
||||
|
||||
@media ${device.tablet} {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
|
||||
.field-label {
|
||||
line-height: unset;
|
||||
margin: 0 0 4px 0;
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const FieldBody = styled.div`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const RadioGroupFieldBody = styled(FieldBody).attrs({
|
||||
className: "radio-group"
|
||||
})``;
|
||||
|
||||
const renderTextField = ({ input, label, isRequired, meta: { touched, error } }) => (
|
||||
<FieldContainer>
|
||||
<Label isRequired={isRequired} error={!!(touched && error)} text={label} className="field-label"/>
|
||||
<FieldBody>
|
||||
<TextInput {...input} type="text" className="field-input"/>
|
||||
</FieldBody>
|
||||
</FieldContainer>
|
||||
);
|
||||
|
||||
const renderPasswordField = ({ input, isDisabled }) => (
|
||||
<TextInput {...input} type="password" autoComplete="new-password" className="field-input" isDisabled={isDisabled} />
|
||||
);
|
||||
|
||||
const renderDateField = ({ input, label, isRequired, meta: { touched, error } }) => (
|
||||
<FieldContainer>
|
||||
<Label isRequired={isRequired} error={touched && !!error} text={label} className="field-label"/>
|
||||
<FieldBody>
|
||||
<DateInput {...input} selected={input.value instanceof Date ? input.value : undefined}/>
|
||||
</FieldBody>
|
||||
</FieldContainer>
|
||||
);
|
||||
|
||||
const renderRadioField = ({ input, label, isChecked }) => (
|
||||
<RadioButton {...input} label={label} isChecked={isChecked}/>
|
||||
);
|
||||
|
||||
const renderDepartmentField = ({ input, onClick }) => (
|
||||
<SelectedItem
|
||||
text={input.value}
|
||||
onClose={onClick}
|
||||
isInline={true}
|
||||
style={{ marginRight: "8px", marginBottom: "8px" }}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderDepartmentsField = ({ fields, label }) => {
|
||||
return (
|
||||
<FieldContainer>
|
||||
<Label text={label} className="field-label"/>
|
||||
<FieldBody>
|
||||
{fields.map((member, index) => (
|
||||
<Field
|
||||
key={`${member}${index}`}
|
||||
name={`${member}.name`}
|
||||
component={renderDepartmentField}
|
||||
onClick={() => fields.remove(index)}
|
||||
/>
|
||||
))}
|
||||
</FieldBody>
|
||||
</FieldContainer>
|
||||
)
|
||||
};
|
||||
|
||||
const renderTextareaField = ({ input }) => (
|
||||
<Textarea {...input} />
|
||||
);
|
||||
|
||||
let UserForm = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
error,
|
||||
handleSubmit,
|
||||
submitting,
|
||||
initialValues,
|
||||
sexValue,
|
||||
passwordTypeValue,
|
||||
passwordError,
|
||||
userType,
|
||||
history,
|
||||
updateProfile,
|
||||
createProfile
|
||||
} = props;
|
||||
|
||||
const employeeWrapperToMemberModel = (profile) => {
|
||||
const comment = profile.notes;
|
||||
const department = profile.groups ? profile.groups.map(group => group.id) : [];
|
||||
const worksFrom = profile.workFrom;
|
||||
|
||||
return {...profile, comment, department, worksFrom};
|
||||
}
|
||||
|
||||
const onCancel = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
const onSubmit = useCallback(async (values) => {
|
||||
try {
|
||||
const member = employeeWrapperToMemberModel(values);
|
||||
|
||||
if (values.id) {
|
||||
await updateProfile(member);
|
||||
} else {
|
||||
await createProfile(member);
|
||||
}
|
||||
|
||||
toastr.success("Success");
|
||||
history.goBack();
|
||||
} catch(error) {
|
||||
toastr.error(error.message);
|
||||
}
|
||||
}, [history, updateProfile, createProfile]);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<MainContainer>
|
||||
<AvatarContainer>
|
||||
{
|
||||
initialValues.id
|
||||
? <Avatar
|
||||
size="max"
|
||||
role={getUserRole(initialValues)}
|
||||
source={initialValues.avatarMax}
|
||||
userName={initialValues.displayName}
|
||||
editing={true}
|
||||
editLabel={t("Edit Photo")}
|
||||
editAction={onEditAvatar}
|
||||
/>
|
||||
: <Avatar
|
||||
size="max"
|
||||
role={userType}
|
||||
editing={true}
|
||||
editLabel={t("AddPhoto")}
|
||||
editAction={onEditAvatar}
|
||||
/>
|
||||
}
|
||||
</AvatarContainer>
|
||||
<MainFieldsContainer>
|
||||
<Field
|
||||
name="firstName"
|
||||
component={renderTextField}
|
||||
label={`${t("FirstName")}:`}
|
||||
isRequired={true}
|
||||
/>
|
||||
<Field
|
||||
name="lastName"
|
||||
component={renderTextField}
|
||||
label={`${t("LastName")}:`}
|
||||
isRequired={true}
|
||||
/>
|
||||
<Field
|
||||
name="email"
|
||||
component={renderTextField}
|
||||
label={`${t("Email")}:`}
|
||||
isRequired={true}
|
||||
/>
|
||||
<FieldContainer>
|
||||
<Label
|
||||
text={`${t("Password")}:`}
|
||||
isRequired={true}
|
||||
error={passwordError}
|
||||
className="field-label"
|
||||
/>
|
||||
<FieldBody>
|
||||
<RadioGroupFieldBody>
|
||||
<Field
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
name="passwordType"
|
||||
value="link"
|
||||
label={t("ActivationLink")}
|
||||
isChecked={passwordTypeValue === "link"}
|
||||
/>
|
||||
<Field
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
name="passwordType"
|
||||
value="temp"
|
||||
label={t("TemporaryPassword")}
|
||||
isChecked={passwordTypeValue === "temp"}
|
||||
/>
|
||||
</RadioGroupFieldBody>
|
||||
<Field
|
||||
name="password"
|
||||
component={renderPasswordField}
|
||||
isDisabled={passwordTypeValue === "link"}
|
||||
/>
|
||||
</FieldBody>
|
||||
</FieldContainer>
|
||||
<Field
|
||||
name="birthday"
|
||||
component={renderDateField}
|
||||
label={`${t("Birthdate")}:`}
|
||||
/>
|
||||
<FieldContainer>
|
||||
<Label
|
||||
text={`${t("Sex")}:`}
|
||||
className="field-label"
|
||||
/>
|
||||
<RadioGroupFieldBody>
|
||||
<Field
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
name="sex"
|
||||
value="male"
|
||||
label={t("SexMale")}
|
||||
isChecked={sexValue === "male"}
|
||||
/>
|
||||
<Field
|
||||
component={renderRadioField}
|
||||
type="radio"
|
||||
name="sex"
|
||||
value="female"
|
||||
label={t("SexFemale")}
|
||||
isChecked={sexValue === "female"}
|
||||
/>
|
||||
</RadioGroupFieldBody>
|
||||
</FieldContainer>
|
||||
<Field
|
||||
name="workFrom"
|
||||
component={renderDateField}
|
||||
label={`EmployedSinceDate:`}
|
||||
/>
|
||||
<Field
|
||||
name="location"
|
||||
component={renderTextField}
|
||||
label={`${t("Location")}:`}
|
||||
/>
|
||||
<Field
|
||||
name="title"
|
||||
component={renderTextField}
|
||||
label={`Position:`}
|
||||
/>
|
||||
<FieldArray
|
||||
name="groups"
|
||||
component={renderDepartmentsField}
|
||||
label={`Departments`}
|
||||
/>
|
||||
</MainFieldsContainer>
|
||||
</MainContainer>
|
||||
<div>
|
||||
<Text.ContentHeader>{t("Comments")}</Text.ContentHeader>
|
||||
<Field
|
||||
name="notes"
|
||||
component={renderTextareaField}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{error && <strong>{error}</strong>}
|
||||
</div>
|
||||
<div style={{marginTop: "60px"}}>
|
||||
<Button
|
||||
label={t("SaveButton")}
|
||||
primary
|
||||
type="submit"
|
||||
isDisabled={submitting}
|
||||
/>
|
||||
<Button
|
||||
label={t("CancelButton")}
|
||||
style={{ marginLeft: "8px" }}
|
||||
isDisabled={submitting}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
const validate = (values) => {
|
||||
const requiredFieldText = "RequiredField";
|
||||
const errors = {};
|
||||
|
||||
if (!values.firstName)
|
||||
errors.firstName = requiredFieldText;
|
||||
|
||||
if (!values.lastName)
|
||||
errors.lastName = requiredFieldText;
|
||||
|
||||
if (!values.email)
|
||||
errors.email = requiredFieldText;
|
||||
|
||||
if (values.passwordType === "temp" && !values.password)
|
||||
errors.password = requiredFieldText;
|
||||
|
||||
return errors
|
||||
};
|
||||
|
||||
UserForm = reduxForm({
|
||||
validate,
|
||||
form: formName,
|
||||
enableReinitialize: true,
|
||||
})(withRouter(UserForm))
|
||||
|
||||
const selector = formValueSelector(formName)
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const sexValue = selector(state, "sex") || "male";
|
||||
const passwordTypeValue = selector(state, "passwordType") || "link";
|
||||
const passwordError =
|
||||
passwordTypeValue !== "link" &&
|
||||
state &&
|
||||
state.form &&
|
||||
state.form.userForm &&
|
||||
state.form.userForm.fields &&
|
||||
state.form.userForm.fields.password &&
|
||||
state.form.userForm.fields.password.touched &&
|
||||
!selector(state, "password");
|
||||
|
||||
return {
|
||||
sexValue,
|
||||
passwordTypeValue,
|
||||
passwordError
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
createProfile,
|
||||
updateProfile
|
||||
}
|
||||
)(UserForm)
|
@ -0,0 +1,231 @@
|
||||
import React from 'react'
|
||||
import { withRouter } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { Avatar, Button, Textarea, Text, toastr } from 'asc-web-components'
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { toEmployeeWrapper, getUserRole, profileEqual, createProfile } from '../../../../../store/profile/actions';
|
||||
import { MainContainer, AvatarContainer, MainFieldsContainer, TextField, PasswordField, DateField, RadioField, DepartmentField } from './userFormFields'
|
||||
|
||||
class CreateUserForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = this.mapPropsToState(props);
|
||||
|
||||
this.validate = this.validate.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.onTextChange = this.onTextChange.bind(this);
|
||||
this.onBirthdayDateChange = this.onBirthdayDateChange.bind(this);
|
||||
this.onWorkFromDateChange = this.onWorkFromDateChange.bind(this);
|
||||
this.onGroupClose = this.onGroupClose.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!profileEqual(this.props.profile, prevProps.profile)) {
|
||||
this.setState(this.mapPropsToState(this.props));
|
||||
}
|
||||
}
|
||||
|
||||
mapPropsToState = (props) => {
|
||||
return {
|
||||
isLoading: false,
|
||||
errors: {
|
||||
firstName: false,
|
||||
lastName: false,
|
||||
email: false,
|
||||
password: false,
|
||||
},
|
||||
profile: {
|
||||
...{ passwordType: "link" },
|
||||
...toEmployeeWrapper(props.profile)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onTextChange(event) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile[event.target.name] = event.target.value;
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onBirthdayDateChange(value) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.birthday = value ? value.toJSON() : null;
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onWorkFromDateChange(value) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.workFrom = value ? value.toJSON() : null;
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onGroupClose(id) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.groups = this.state.groups.filter((group) => group.id !== id);
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
validate() {
|
||||
const errors = {
|
||||
firstName: !this.state.profile.firstName,
|
||||
lastName: !this.state.profile.lastName,
|
||||
email: !this.state.profile.email,
|
||||
password: this.state.profile.passwordType === "temp" && !this.state.profile.password
|
||||
};
|
||||
const hasError = errors.firstName || errors.lastName || errors.email || errors.password;
|
||||
this.setState({errors: errors});
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
if(!this.validate())
|
||||
return false;
|
||||
|
||||
this.setState({isLoading: true});
|
||||
|
||||
this.props.createProfile(this.state.profile)
|
||||
.then(() => {
|
||||
toastr.success("Success");
|
||||
this.props.history.goBack();
|
||||
})
|
||||
.catch((error) => {
|
||||
toastr.error(error.message)
|
||||
this.setState({isLoading: false})
|
||||
});
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.props.history.goBack();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<MainContainer>
|
||||
<AvatarContainer>
|
||||
<Avatar
|
||||
size="max"
|
||||
role={getUserRole(this.state.profile)}
|
||||
editing={true}
|
||||
editLabel={this.props.t("AddPhoto")}
|
||||
/>
|
||||
</AvatarContainer>
|
||||
<MainFieldsContainer>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.firstName}
|
||||
labelText={`${this.props.t("FirstName")}:`}
|
||||
inputName="firstName"
|
||||
inputValue={this.state.profile.firstName}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.lastName}
|
||||
labelText={`${this.props.t("LastName")}:`}
|
||||
inputName="lastName"
|
||||
inputValue={this.state.profile.lastName}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.email}
|
||||
labelText={`${this.props.t("Email")}:`}
|
||||
inputName="email"
|
||||
inputValue={this.state.profile.email}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<PasswordField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.password}
|
||||
labelText={`${this.props.t("Password")}:`}
|
||||
radioName="passwordType"
|
||||
radioValue={this.state.profile.passwordType}
|
||||
radioOptions={[
|
||||
{ value: 'link', label: this.props.t("ActivationLink")},
|
||||
{ value: 'temp', label: this.props.t("TemporaryPassword")}
|
||||
]}
|
||||
radioIsDisabled={this.state.isLoading}
|
||||
radioOnChange={this.onTextChange}
|
||||
inputName="password"
|
||||
inputValue={this.state.profile.password}
|
||||
inputIsDisabled={this.state.isLoading || this.state.profile.passwordType === "link"}
|
||||
inputOnChange={this.onTextChange}
|
||||
/>
|
||||
<DateField
|
||||
labelText={`${this.props.t("Birthdate")}:`}
|
||||
inputName="birthday"
|
||||
inputValue={this.state.profile.birthday ? new Date(this.state.profile.birthday) : undefined}
|
||||
inputIsDisabled={this.state.isLoading}
|
||||
inputOnChange={this.onBirthdayDateChange}
|
||||
/>
|
||||
<RadioField
|
||||
labelText={`${this.props.t("Sex")}:`}
|
||||
radioName="sex"
|
||||
radioValue={this.state.profile.sex}
|
||||
radioOptions={[
|
||||
{ value: 'male', label: this.props.t("SexMale")},
|
||||
{ value: 'female', label: this.props.t("SexFemale")}
|
||||
]}
|
||||
radioIsDisabled={this.state.isLoading}
|
||||
radioOnChange={this.onTextChange}
|
||||
/>
|
||||
<DateField
|
||||
labelText={`${this.props.t("EmployedSinceDate")}:`}
|
||||
inputName="workFrom"
|
||||
inputValue={this.state.profile.workFrom ? new Date(this.state.profile.workFrom) : undefined}
|
||||
inputIsDisabled={this.state.isLoading}
|
||||
inputOnChange={this.onWorkFromDateChange}
|
||||
/>
|
||||
<TextField
|
||||
labelText={`${this.props.t("Location")}:`}
|
||||
inputName="location"
|
||||
inputValue={this.state.profile.location}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<TextField
|
||||
labelText={`${this.props.t("Position")}:`}
|
||||
inputName="title"
|
||||
inputValue={this.state.profile.title}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<DepartmentField
|
||||
labelText={`${this.props.t("Departments")}:`}
|
||||
departments={this.state.profile.groups}
|
||||
onClose={this.onGroupClose}
|
||||
/>
|
||||
</MainFieldsContainer>
|
||||
</MainContainer>
|
||||
<div>
|
||||
<Text.ContentHeader>{this.props.t("Comments")}</Text.ContentHeader>
|
||||
<Textarea name="notes" value={this.state.profile.notes} isDisabled={this.state.isLoading} onChange={this.onTextChange}/>
|
||||
</div>
|
||||
<div style={{marginTop: "60px"}}>
|
||||
<Button label={this.props.t("SaveButton")} onClick={this.handleSubmit} primary isDisabled={this.state.isLoading} size="big"/>
|
||||
<Button label={this.props.t("CancelButton")} onClick={this.onCancel} isDisabled={this.state.isLoading} size="big" style={{ marginLeft: "8px" }}/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
profile: state.profile.targetUser
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
createProfile
|
||||
}
|
||||
)(withRouter(withTranslation()(CreateUserForm)));
|
@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import UserForm from './Form/userForm'
|
||||
|
||||
|
||||
const SectionBodyContent = (props) => {
|
||||
const {profile, userType} = props;
|
||||
|
||||
// if(profile.birthday)
|
||||
// profile.birthday = new Date(profile.birthday).toLocaleDateString();
|
||||
|
||||
// if(profile.workFrom)
|
||||
// profile.workFrom = new Date(profile.workFrom).toLocaleDateString();
|
||||
|
||||
return (
|
||||
<UserForm initialValues={profile} userType={userType}/>
|
||||
);
|
||||
};
|
||||
|
||||
SectionBodyContent.propTypes = {
|
||||
profile: PropTypes.object,
|
||||
userType: PropTypes.oneOf(["user", "guest"])
|
||||
};
|
||||
|
||||
SectionBodyContent.defaultProps = {
|
||||
profile: null,
|
||||
userType: "user"
|
||||
}
|
||||
|
||||
export default SectionBodyContent;
|
@ -0,0 +1,233 @@
|
||||
import React from 'react'
|
||||
import { withRouter } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { Avatar, Button, Textarea, Text, toastr } from 'asc-web-components'
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { toEmployeeWrapper, getUserRole, profileEqual, updateProfile } from '../../../../../store/profile/actions';
|
||||
import { MainContainer, AvatarContainer, MainFieldsContainer, TextField, PasswordField, DateField, RadioField, DepartmentField } from './userFormFields'
|
||||
|
||||
class UpdateUserForm extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = this.mapPropsToState(props);
|
||||
|
||||
this.validate = this.validate.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.onTextChange = this.onTextChange.bind(this);
|
||||
this.onBirthdayDateChange = this.onBirthdayDateChange.bind(this);
|
||||
this.onWorkFromDateChange = this.onWorkFromDateChange.bind(this);
|
||||
this.onGroupClose = this.onGroupClose.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!profileEqual(this.props.profile, prevProps.profile)) {
|
||||
this.setState(this.mapPropsToState(this.props));
|
||||
}
|
||||
}
|
||||
|
||||
mapPropsToState = (props) => {
|
||||
return {
|
||||
isLoading: false,
|
||||
errors: {
|
||||
firstName: false,
|
||||
lastName: false,
|
||||
email: false,
|
||||
password: false,
|
||||
},
|
||||
profile: {
|
||||
...{ passwordType: "link" },
|
||||
...toEmployeeWrapper(props.profile)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onTextChange(event) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile[event.target.name] = event.target.value;
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onBirthdayDateChange(value) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.birthday = value ? value.toJSON() : null;
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onWorkFromDateChange(value) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.workFrom = value ? value.toJSON() : null;
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onGroupClose(id) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.groups = this.state.groups.filter((group) => group.id !== id);
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
validate() {
|
||||
const errors = {
|
||||
firstName: !this.state.profile.firstName,
|
||||
lastName: !this.state.profile.lastName,
|
||||
email: !this.state.profile.email,
|
||||
password: this.state.profile.passwordType === "temp" && !this.state.profile.password
|
||||
};
|
||||
const hasError = errors.firstName || errors.lastName || errors.email || errors.password;
|
||||
this.setState({errors: errors});
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
if(!this.validate())
|
||||
return false;
|
||||
|
||||
this.setState({isLoading: true});
|
||||
|
||||
this.props.updateProfile(this.state.profile)
|
||||
.then(() => {
|
||||
toastr.success("Success");
|
||||
this.props.history.goBack();
|
||||
})
|
||||
.catch((error) => {
|
||||
toastr.error(error.message)
|
||||
this.setState({isLoading: false})
|
||||
});
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.props.history.goBack();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<MainContainer>
|
||||
<AvatarContainer>
|
||||
<Avatar
|
||||
size="max"
|
||||
role={getUserRole(this.state.profile)}
|
||||
source={this.state.profile.avatarMax}
|
||||
userName={this.state.profile.displayName}
|
||||
editing={true}
|
||||
editLabel={this.props.t("EditPhoto")}
|
||||
/>
|
||||
</AvatarContainer>
|
||||
<MainFieldsContainer>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.firstName}
|
||||
labelText={`${this.props.t("FirstName")}:`}
|
||||
inputName="firstName"
|
||||
inputValue={this.state.profile.firstName}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.lastName}
|
||||
labelText={`${this.props.t("LastName")}:`}
|
||||
inputName="lastName"
|
||||
inputValue={this.state.profile.lastName}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.email}
|
||||
labelText={`${this.props.t("Email")}:`}
|
||||
inputName="email"
|
||||
inputValue={this.state.profile.email}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<PasswordField
|
||||
isRequired={true}
|
||||
hasError={this.state.errors.password}
|
||||
labelText={`${this.props.t("Password")}:`}
|
||||
radioName="passwordType"
|
||||
radioValue={this.state.profile.passwordType}
|
||||
radioOptions={[
|
||||
{ value: 'link', label: this.props.t("ActivationLink")},
|
||||
{ value: 'temp', label: this.props.t("TemporaryPassword")}
|
||||
]}
|
||||
radioIsDisabled={this.state.isLoading}
|
||||
radioOnChange={this.onTextChange}
|
||||
inputName="password"
|
||||
inputValue={this.state.profile.password}
|
||||
inputIsDisabled={this.state.isLoading || this.state.profile.passwordType === "link"}
|
||||
inputOnChange={this.onTextChange}
|
||||
/>
|
||||
<DateField
|
||||
labelText={`${this.props.t("Birthdate")}:`}
|
||||
inputName="birthday"
|
||||
inputValue={this.state.profile.birthday ? new Date(this.state.profile.birthday) : undefined}
|
||||
inputIsDisabled={this.state.isLoading}
|
||||
inputOnChange={this.onBirthdayDateChange}
|
||||
/>
|
||||
<RadioField
|
||||
labelText={`${this.props.t("Sex")}:`}
|
||||
radioName="sex"
|
||||
radioValue={this.state.profile.sex}
|
||||
radioOptions={[
|
||||
{ value: 'male', label: this.props.t("SexMale")},
|
||||
{ value: 'female', label: this.props.t("SexFemale")}
|
||||
]}
|
||||
radioIsDisabled={this.state.isLoading}
|
||||
radioOnChange={this.onTextChange}
|
||||
/>
|
||||
<DateField
|
||||
labelText={`${this.props.t("EmployedSinceDate")}:`}
|
||||
inputName="workFrom"
|
||||
inputValue={this.state.profile.workFrom ? new Date(this.state.profile.workFrom) : undefined}
|
||||
inputIsDisabled={this.state.isLoading}
|
||||
inputOnChange={this.onWorkFromDateChange}
|
||||
/>
|
||||
<TextField
|
||||
labelText={`${this.props.t("Location")}:`}
|
||||
inputName="location"
|
||||
inputValue={this.state.profile.location}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<TextField
|
||||
labelText={`${this.props.t("Position")}:`}
|
||||
inputName="title"
|
||||
inputValue={this.state.profile.title}
|
||||
isDisabled={this.state.isLoading}
|
||||
onChange={this.onTextChange}
|
||||
/>
|
||||
<DepartmentField
|
||||
labelText={`${this.props.t("Departments")}:`}
|
||||
departments={this.state.profile.groups}
|
||||
onClose={this.onGroupClose}
|
||||
/>
|
||||
</MainFieldsContainer>
|
||||
</MainContainer>
|
||||
<div>
|
||||
<Text.ContentHeader>{this.props.t("Comments")}</Text.ContentHeader>
|
||||
<Textarea name="notes" value={this.state.profile.notes} isDisabled={this.state.isLoading} onChange={this.onTextChange}/>
|
||||
</div>
|
||||
<div style={{marginTop: "60px"}}>
|
||||
<Button label={this.props.t("SaveButton")} onClick={this.handleSubmit} primary isDisabled={this.state.isLoading} size="big"/>
|
||||
<Button label={this.props.t("CancelButton")} onClick={this.onCancel} isDisabled={this.state.isLoading} size="big" style={{ marginLeft: "8px" }}/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
profile: state.profile.targetUser
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
updateProfile
|
||||
}
|
||||
)(withRouter(withTranslation()(UpdateUserForm)));
|
@ -0,0 +1,79 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components';
|
||||
import { device, FieldContainer, TextInput, DateInput, RadioButtonGroup, SelectedItem } from 'asc-web-components'
|
||||
|
||||
const MainContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@media ${device.tablet} {
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
const AvatarContainer = styled.div`
|
||||
margin: 0 32px 32px 0;
|
||||
width: 160px;
|
||||
`;
|
||||
|
||||
const MainFieldsContainer = styled.div`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const TextField = React.memo((props) => {
|
||||
const {isRequired, hasError, labelText, inputName, inputValue, isDisabled, onChange} = props;
|
||||
return (
|
||||
<FieldContainer isRequired={isRequired} hasError={hasError} labelText={labelText}>
|
||||
<TextInput name={inputName} value={inputValue} isDisabled={isDisabled} hasError={hasError} onChange={onChange} className="field-input"/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
|
||||
const PasswordField = React.memo((props) => {
|
||||
const {isRequired, hasError, labelText, radioName, radioValue, radioOptions, radioIsDisabled, radioOnChange, inputName, inputValue, inputIsDisabled, inputOnChange} = props;
|
||||
return (
|
||||
<FieldContainer isRequired={isRequired} hasError={hasError} labelText={labelText}>
|
||||
<RadioButtonGroup name={radioName} selected={radioValue} options={radioOptions} isDisabled={radioIsDisabled} onClick={radioOnChange} className="radio-group"/>
|
||||
<TextInput name={inputName} type="password" value={inputValue} isDisabled={inputIsDisabled} hasError={hasError} onChange={inputOnChange} className="field-input"/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
|
||||
const DateField = React.memo((props) => {
|
||||
const {isRequired, hasError, labelText, inputName, inputValue, inputIsDisabled, inputOnChange} = props;
|
||||
return (
|
||||
<FieldContainer isRequired={isRequired} hasError={hasError} labelText={labelText}>
|
||||
<DateInput name={inputName} selected={inputValue} disabled={inputIsDisabled} hasError={hasError} onChange={inputOnChange} className="field-input"/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
|
||||
const RadioField = React.memo((props) => {
|
||||
const {isRequired, hasError, labelText, radioName, radioValue, radioOptions, radioIsDisabled, radioOnChange} = props;
|
||||
return (
|
||||
<FieldContainer isRequired={isRequired} hasError={hasError} labelText={labelText}>
|
||||
<RadioButtonGroup name={radioName} selected={radioValue} options={radioOptions} isDisabled={radioIsDisabled} onClick={radioOnChange} className="radio-group"/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
|
||||
const DepartmentField = React.memo((props) => {
|
||||
const {isRequired, hasError, labelText, departments, onClose} = props;
|
||||
return (
|
||||
departments && departments.length
|
||||
? <FieldContainer isRequired={isRequired} hasError={hasError} labelText={labelText}>
|
||||
{departments.map((department, index) => (
|
||||
<SelectedItem
|
||||
key={`user_group_${department.id}`}
|
||||
text={department.name}
|
||||
onClose={() => { onClose(department.id) }}
|
||||
isInline={true}
|
||||
style={{ marginRight: "8px", marginBottom: "8px" }}
|
||||
/>
|
||||
))}
|
||||
</FieldContainer>
|
||||
: ""
|
||||
);
|
||||
});
|
||||
|
||||
export { MainContainer, AvatarContainer, MainFieldsContainer, TextField, PasswordField, DateField, RadioField, DepartmentField }
|
@ -1,50 +1,44 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from "react-router";
|
||||
import PropTypes from "prop-types";
|
||||
import { IconButton, Text } from 'asc-web-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const wrapperStyle = {
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
};
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
align-Items: center;
|
||||
`;
|
||||
|
||||
const textStyle = {
|
||||
marginLeft: "16px"
|
||||
};
|
||||
const Header = styled(Text.ContentHeader)`
|
||||
margin-left: 16px;
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = (props) => {
|
||||
const {profile, history, userType, settings} = props;
|
||||
const {profile, history, settings} = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const headerText = profile && profile.displayName
|
||||
? profile.displayName
|
||||
: userType === "user"
|
||||
? t('NewEmployee')
|
||||
: t('NewGuest');
|
||||
: profile.isVisitor
|
||||
? t('NewGuest')
|
||||
: t('NewEmployee');
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
history.push(settings.homepage)
|
||||
}, [history, settings]);
|
||||
|
||||
return (
|
||||
<div style={wrapperStyle}>
|
||||
<IconButton iconName={'ArrowPathIcon'} size="16" onClick={() => history.push(settings.homepage)}/>
|
||||
<Text.ContentHeader style={textStyle}>{headerText}</Text.ContentHeader>
|
||||
</div>
|
||||
<Wrapper>
|
||||
<IconButton iconName={'ArrowPathIcon'} size="16" onClick={onClick}/>
|
||||
<Header>{headerText}</Header>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
SectionHeaderContent.propTypes = {
|
||||
profile: PropTypes.object,
|
||||
history: PropTypes.object.isRequired,
|
||||
userType: PropTypes.oneOf(["user", "guest"])
|
||||
};
|
||||
|
||||
SectionHeaderContent.defaultProps = {
|
||||
profile: null,
|
||||
userType: "user"
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
profile: state.profile.targetUser,
|
||||
settings: state.auth.settings
|
||||
};
|
||||
};
|
||||
|
@ -1,2 +1,3 @@
|
||||
export { default as SectionHeaderContent } from './Header';
|
||||
export { default as SectionBodyContent } from './Body';
|
||||
export { default as CreateUserForm } from './Body/createUserForm';
|
||||
export { default as UpdateUserForm } from './Body/updateUserForm';
|
@ -3,24 +3,19 @@ import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { PageLayout, Loader } from "asc-web-components";
|
||||
import { ArticleHeaderContent, ArticleMainButtonContent, ArticleBodyContent } from '../../Article';
|
||||
import { SectionHeaderContent, SectionBodyContent } from './Section';
|
||||
import { SectionHeaderContent, CreateUserForm, UpdateUserForm } from './Section';
|
||||
import { setProfile, fetchProfile, resetProfile } from '../../../store/profile/actions';
|
||||
import i18n from "./i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
|
||||
class ProfileAction extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { match, setProfile, fetchProfile } = this.props;
|
||||
const { userId, type } = match.params;
|
||||
|
||||
if (!userId) {
|
||||
setProfile({ isVisitor: type === "guest" });
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
fetchProfile(userId);
|
||||
}
|
||||
}
|
||||
@ -33,37 +28,31 @@ class ProfileAction extends React.Component {
|
||||
|
||||
if (!userId && type !== prevType) {
|
||||
setProfile({ isVisitor: type === "guest" });
|
||||
}
|
||||
else if (userId !== prevUserId) {
|
||||
} else if (userId !== prevUserId) {
|
||||
fetchProfile(userId);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.resetProfile();
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("ProfileAction render")
|
||||
|
||||
const { profile, match } = this.props;
|
||||
const { type } = match.params;
|
||||
const { profile } = this.props;
|
||||
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
{profile
|
||||
? <PageLayout
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||
articleBodyContent={<ArticleBodyContent />}
|
||||
sectionHeaderContent={<SectionHeaderContent profile={profile} userType={type} />}
|
||||
sectionBodyContent={<SectionBodyContent profile={profile} userType={type} />}
|
||||
/>
|
||||
: <PageLayout
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||
articleBodyContent={<ArticleBodyContent />}
|
||||
sectionBodyContent={<Loader className="pageLoader" type="rombs" size={40} />}
|
||||
? <PageLayout
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||
articleBodyContent={<ArticleBodyContent />}
|
||||
sectionHeaderContent={<SectionHeaderContent />}
|
||||
sectionBodyContent={profile.id ? <UpdateUserForm /> : <CreateUserForm />}
|
||||
/>
|
||||
: <PageLayout
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||
articleBodyContent={<ArticleBodyContent />}
|
||||
sectionBodyContent={<Loader className="pageLoader" type="rombs" size={40} />}
|
||||
/>}
|
||||
</I18nextProvider>
|
||||
);
|
||||
@ -73,8 +62,9 @@ class ProfileAction extends React.Component {
|
||||
ProfileAction.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
profile: PropTypes.object,
|
||||
setProfile: PropTypes.func.isRequired,
|
||||
fetchProfile: PropTypes.func.isRequired,
|
||||
setProfile: PropTypes.func.isRequired
|
||||
resetProfile: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
@ -18,5 +18,9 @@
|
||||
"SexFemale": "Female",
|
||||
"RequiredField": "Required field",
|
||||
"NewEmployee": "New employee",
|
||||
"NewGuest": "New guest"
|
||||
"NewGuest": "New guest",
|
||||
|
||||
"EmployedSinceDate": "EmployedSinceDate",
|
||||
"Position": "Position",
|
||||
"Departments": "Departments"
|
||||
}
|
@ -94,6 +94,16 @@ export function fetchPeople(filter) {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPeopleByFilter(dispatch, filter) {
|
||||
let filterData = (filter && filter.clone()) || Filter.getDefault();
|
||||
return api.getUserList(filterData).then(res => {
|
||||
filterData.total = res.data.total;
|
||||
dispatch(setFilter(filterData));
|
||||
return dispatch(setUsers(res.data.response));
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchPeopleAsync(dispatch, filter = null) {
|
||||
let filterData = (filter && filter.clone()) || Filter.getDefault();
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as api from "../../store/services/api";
|
||||
import { isMe } from '../auth/selectors';
|
||||
import { getUserByUserName } from '../people/selectors';
|
||||
import { fetchPeopleAsync } from "../people/actions";
|
||||
import { fetchPeopleByFilter } from "../people/actions";
|
||||
|
||||
export const SET_PROFILE = 'SET_PROFILE';
|
||||
export const CLEAN_PROFILE = 'CLEAN_PROFILE';
|
||||
@ -19,59 +19,119 @@ export function resetProfile() {
|
||||
};
|
||||
};
|
||||
|
||||
function checkResponseError(res) {
|
||||
if(res && res.data && res.data.error){
|
||||
export function checkResponseError(res) {
|
||||
if (res && res.data && res.data.error) {
|
||||
console.error(res.data.error);
|
||||
throw new Error(res.data.error.message);
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchProfile(userName) {
|
||||
return async (dispatch, getState) => {
|
||||
try {
|
||||
const { auth, people } = getState();
|
||||
|
||||
if (isMe(auth.user, userName)) {
|
||||
dispatch(setProfile(auth.user));
|
||||
} else {
|
||||
const user = getUserByUserName(people.users, userName);
|
||||
if (!user) {
|
||||
const res = await api.getUser(userName);
|
||||
dispatch(setProfile(res.data.response))
|
||||
}
|
||||
else
|
||||
dispatch(setProfile(user));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
export function getUserRole(profile) {
|
||||
if(profile.isOwner) return "owner";
|
||||
if(profile.isAdmin) return "admin";
|
||||
if(profile.isVisitor) return "guest";
|
||||
return "user";
|
||||
};
|
||||
|
||||
export function profileEqual(profileA, profileB) {
|
||||
const keys = Object.keys(profileA);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
|
||||
if (key === "groups") {
|
||||
if (profileA[key].length !== profileB[key].length)
|
||||
return false;
|
||||
|
||||
const groupsA = profileA[key].map(group => group.id);
|
||||
const groupsB = profileA[key].map(group => group.id);
|
||||
|
||||
for (let j = 0; j < groupsA.length; j++) {
|
||||
if (!groupsB.includes(groupsA[j]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(profileA[key] !== profileB[key])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function toEmployeeWrapper(profile) {
|
||||
const emptyData = {
|
||||
id: "",
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
birthday: "",
|
||||
sex: "male",
|
||||
workFrom: "",
|
||||
location: "",
|
||||
title: "",
|
||||
groups: [],
|
||||
notes: ""
|
||||
};
|
||||
|
||||
return { ...emptyData, ...profile };
|
||||
}
|
||||
|
||||
export function employeeWrapperToMemberModel(profile) {
|
||||
const comment = profile.notes;
|
||||
const department = profile.groups ? profile.groups.map(group => group.id) : [];
|
||||
const worksFrom = profile.workFrom;
|
||||
|
||||
return {...profile, comment, department, worksFrom};
|
||||
}
|
||||
|
||||
export function fetchProfile(userName) {
|
||||
return (dispatch, getState) => {
|
||||
const { auth, people } = getState();
|
||||
|
||||
if (isMe(auth.user, userName)) {
|
||||
dispatch(setProfile(auth.user));
|
||||
} else {
|
||||
const user = getUserByUserName(people.users, userName);
|
||||
if (!user) {
|
||||
api.getUser(userName).then(res => {
|
||||
checkResponseError(res);
|
||||
dispatch(setProfile(res.data.response));
|
||||
});
|
||||
} else {
|
||||
dispatch(setProfile(user));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function createProfile(profile) {
|
||||
return async (dispatch, getState) => {
|
||||
return (dispatch, getState) => {
|
||||
const {people} = getState();
|
||||
const {filter} = people;
|
||||
const member = employeeWrapperToMemberModel(profile);
|
||||
|
||||
const res = await api.createUser(profile);
|
||||
|
||||
checkResponseError(res);
|
||||
|
||||
dispatch(setProfile(res.data.response))
|
||||
await fetchPeopleAsync(dispatch, filter);
|
||||
return api.createUser(member).then(res => {
|
||||
checkResponseError(res);
|
||||
return Promise.resolve(dispatch(setProfile(res.data.response)));
|
||||
}).then(() => {
|
||||
return fetchPeopleByFilter(dispatch, filter);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function updateProfile(profile) {
|
||||
return async (dispatch, getState) => {
|
||||
return (dispatch, getState) => {
|
||||
const {people} = getState();
|
||||
const {filter} = people;
|
||||
const member = employeeWrapperToMemberModel(profile);
|
||||
|
||||
const res = await api.updateUser(profile);
|
||||
|
||||
checkResponseError(res);
|
||||
|
||||
dispatch(setProfile(res.data.response))
|
||||
await fetchPeopleAsync(dispatch, filter);
|
||||
return api.updateUser(member).then(res => {
|
||||
checkResponseError(res);
|
||||
return Promise.resolve(dispatch(setProfile(res.data.response)));
|
||||
}).then(() => {
|
||||
return fetchPeopleByFilter(dispatch, filter);
|
||||
});
|
||||
};
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-components",
|
||||
"version": "1.0.15",
|
||||
"version": "1.0.16",
|
||||
"description": "Ascensio System SIA component library",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/asc-web-components.cjs.js",
|
||||
|
@ -0,0 +1,57 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components';
|
||||
import device from '../device'
|
||||
import Label from '../label'
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0 0 16px 0;
|
||||
|
||||
.field-label {
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
line-height: 32px;
|
||||
display: flex;
|
||||
|
||||
label:not(:first-child) {
|
||||
margin-left: 33px;
|
||||
}
|
||||
}
|
||||
|
||||
@media ${device.tablet} {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
|
||||
.field-label {
|
||||
line-height: unset;
|
||||
margin: 0 0 4px 0;
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Body = styled.div`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const FieldContainer = React.memo((props) => {
|
||||
const {isRequired, hasError, labelText, className, children} = props;
|
||||
return (
|
||||
<Container className={className}>
|
||||
<Label isRequired={isRequired} error={hasError} text={labelText} className="field-label"/>
|
||||
<Body>{children}</Body>
|
||||
</Container>
|
||||
);
|
||||
});
|
||||
|
||||
export default FieldContainer
|
@ -51,3 +51,4 @@ export { default as CustomScrollbarsVirtualList } from './components/scrollbar/c
|
||||
export { default as RowContent } from './components/row-content'
|
||||
export { default as NewCalendar } from './components/calendar-new'
|
||||
export { default as AdvancedSelector } from './components/advanced-selector'
|
||||
export { default as FieldContainer } from './components/field-container'
|
29
web/ASC.Web.Storybook/stories/field-container/base/README.md
Normal file
29
web/ASC.Web.Storybook/stories/field-container/base/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# FieldContainer
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { FieldContainer } from 'asc-web-components';
|
||||
```
|
||||
|
||||
### <Label>
|
||||
|
||||
Responsive form field container
|
||||
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
|
||||
<FieldContainer labelText="Name:">
|
||||
<TextInput/>
|
||||
</FieldContainer>
|
||||
|
||||
```
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------| -------- | :------: | -------| ------- | -------------------------------------------- |
|
||||
| `isRequired`| `bool` | - | - | false | Indicates that the field is required to fill |
|
||||
| `hasError` | `bool` | - | - | - | Indicates that the field is incorrect |
|
||||
| `labelText` | `string` | - | - | - | Field label text |
|
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { StringValue } from 'react-values';
|
||||
import { text, boolean, withKnobs } from '@storybook/addon-knobs/react';
|
||||
import { FieldContainer, TextInput } from 'asc-web-components';
|
||||
import Section from '../../../.storybook/decorators/section';
|
||||
import withReadme from 'storybook-readme/with-readme';
|
||||
import Readme from './README.md';
|
||||
|
||||
storiesOf('Components|FieldContainer', module)
|
||||
.addDecorator(withKnobs)
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add('base', () => (
|
||||
<StringValue
|
||||
onChange={e => {
|
||||
action('onChange')(e);
|
||||
}}
|
||||
>
|
||||
{({ value, set }) => (
|
||||
<Section>
|
||||
<FieldContainer
|
||||
isRequired={boolean('isRequired', false)}
|
||||
hasError={boolean('hasError', false)}
|
||||
labelText={text('labelText', 'Name:')}
|
||||
>
|
||||
<TextInput
|
||||
value={value}
|
||||
hasError={boolean('hasError', false)}
|
||||
className="field-input"
|
||||
onChange={e => {
|
||||
set(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</FieldContainer>
|
||||
</Section>
|
||||
)}
|
||||
</StringValue>
|
||||
));
|
Loading…
Reference in New Issue
Block a user