Merge branch 'feature/selectors-refactoring' of github.com:ONLYOFFICE/AppServer into feature/selectors-refactoring

This commit is contained in:
Ilya Oleshko 2020-10-08 16:58:39 +03:00
commit 0e4698b8f0
23 changed files with 884 additions and 518 deletions

View File

@ -1,7 +1,10 @@
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { utils, TreeMenu, TreeNode, Icons, Link } from "asc-web-components";
import { selectGroup } from "../../../store/people/actions";
import {
selectGroup,
setIsVisibleDataLossDialog,
} from "../../../store/people/actions";
import { getSelectedGroup } from "../../../store/people/selectors";
import { withTranslation, I18nextProvider } from "react-i18next";
import {
@ -100,16 +103,25 @@ class ArticleBodyContent extends React.Component {
return false;
}
onSelectHandler = (data) => {
const { editingForm, setIsVisibleDataLossDialog } = this.props;
onSelect = (data) => {
const { selectGroup } = this.props;
this.changeTitleDocument(data);
selectGroup(
data && data.length === 1 && data[0] !== "root" ? data[0] : null
);
if (editingForm.isEdit) {
setIsVisibleDataLossDialog(true, this.onSelect(data));
} else {
this.onSelect(data)();
}
};
onSelect = (data) => {
return () => {
const { selectGroup } = this.props;
this.changeTitleDocument(data);
selectGroup(
data && data.length === 1 && data[0] !== "root" ? data[0] : null
);
};
};
switcherIcon = (obj) => {
if (obj.isLeaf) {
return null;
@ -141,7 +153,7 @@ class ArticleBodyContent extends React.Component {
showIcon={true}
defaultExpandAll={true}
switcherIcon={this.switcherIcon}
onSelect={this.onSelect}
onSelect={this.onSelectHandler}
selectedKeys={selectedKeys}
isFullFillSelection={false}
gapBetweenNodes="22"
@ -220,6 +232,7 @@ function mapStateToProps(state) {
const { isLoaded, settings } = state.auth;
const { customNames } = settings;
const { groupsCaption } = customNames;
const { editingForm } = state.people;
return {
data: getTreeGroups(groups, groupsCaption),
@ -229,7 +242,11 @@ function mapStateToProps(state) {
groups,
isAdmin: isAdmin(state),
isLoaded,
editingForm
};
}
export default connect(mapStateToProps, { selectGroup })(BodyContent);
export default connect(mapStateToProps, {
selectGroup,
setIsVisibleDataLossDialog,
})(BodyContent);

View File

@ -0,0 +1,106 @@
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { ModalDialog, Button, Text } from "asc-web-components";
import { withTranslation } from "react-i18next";
import { utils } from "asc-web-common";
import ModalDialogContainer from "../ModalDialogContainer";
import { createI18N } from "../../../helpers/i18n";
import {
setIsVisibleDataLossDialog,
setIsEditingForm,
} from "../../../store/people/actions";
const i18n = createI18N({
page: "DataLossWarningDialog",
localesPath: "dialogs/DataLossWarningDialog",
});
const { changeLanguage } = utils;
class DataLossWarningDialogComponent extends React.Component {
constructor(props) {
super(props);
changeLanguage(i18n);
}
onClose = () => {
const { setIsVisibleDataLossDialog } = this.props;
setIsVisibleDataLossDialog(false);
};
onSubmit = () => {
const {
onContinue,
setIsVisibleDataLossDialog,
setIsEditingForm,
editingForm,
} = this.props;
setIsVisibleDataLossDialog(false, null);
setIsEditingForm(false);
if (editingForm.callback) {
editingForm.callback();
} else {
onContinue && onContinue();
}
};
render() {
const { t, editingForm } = this.props;
return (
<ModalDialogContainer>
<ModalDialog
visible={editingForm.isVisibleDataLossDialog}
onClose={this.onClose}
>
<ModalDialog.Header>{t("DataLossWarningHeader")}</ModalDialog.Header>
<ModalDialog.Body>
<Text fontSize="13px">{t("DataLossWarningBody")}</Text>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
key="LeaveForm"
label={t("DataLossWarningLeaveBtn")}
size="medium"
primary={true}
onClick={this.onSubmit}
/>
<Button
className="button-dialog"
key="StayOnPage"
label={t("DataLossWarningCancelBtn")}
size="medium"
onClick={this.onClose}
/>
</ModalDialog.Footer>
</ModalDialog>
</ModalDialogContainer>
);
}
}
const DataLossWarningDialogTranslated = withTranslation()(
DataLossWarningDialogComponent
);
const DataLossWarningDialog = (props) => (
<DataLossWarningDialogTranslated i18n={i18n} {...props} />
);
DataLossWarningDialog.propTypes = {
editingForm: PropTypes.object.isRequired,
onContinue: PropTypes.func.isRequired,
};
function mapStateToProps(state) {
return {
editingForm: state.people.editingForm,
};
}
export default connect(mapStateToProps, {
setIsVisibleDataLossDialog,
setIsEditingForm,
})(DataLossWarningDialog);

View File

@ -0,0 +1,6 @@
{
"DataLossWarningHeader": "Leave the page?",
"DataLossWarningBody": "Changes you made may not be saved.",
"DataLossWarningLeaveBtn": "Leave",
"DataLossWarningCancelBtn": "Cancel"
}

View File

@ -0,0 +1,6 @@
{
"DataLossWarningHeader": "Покинуть страницу?",
"DataLossWarningBody": "Внесенные вами изменения могут быть не сохранены.",
"DataLossWarningLeaveBtn": "Покинуть",
"DataLossWarningCancelBtn": "Отмена"
}

View File

@ -8,6 +8,7 @@ import InviteDialog from "./InviteDialog";
import SendInviteDialog from "./SendInviteDialog";
import ChangeUserStatusDialog from "./ChangeUserStatusDialog";
import ChangeUserTypeDialog from "./ChangeUserTypeDialog";
import DataLossWarningDialog from "./DataLossWarningDialog";
export {
ChangeEmailDialog,
@ -19,5 +20,6 @@ export {
InviteDialog,
SendInviteDialog,
ChangeUserStatusDialog,
ChangeUserTypeDialog
ChangeUserTypeDialog,
DataLossWarningDialog,
};

View File

@ -14,7 +14,7 @@ import {
resetGroup,
updateGroup,
} from "../../../../../store/group/actions";
import { selectGroup } from "../../../../../store/people/actions";
import { selectGroup, setFilter } from "../../../../../store/people/actions";
import { GUID_EMPTY } from "../../../../../helpers/constants";
import PropTypes from "prop-types";
@ -241,10 +241,10 @@ class SectionBodyContent extends React.Component {
};
onCancel = () => {
const { history, resetGroup, settings } = this.props;
const { resetGroup, filter, setFilter } = this.props;
resetGroup();
history.push(`${settings.homepage}/`);
setFilter(filter);
};
onSelectedItemClose = (member) => {
@ -490,6 +490,7 @@ function mapStateToProps(state) {
groupCaption,
me: getCurrentUser(state),
currentModuleName,
filter: state.people.filter,
};
}
@ -498,4 +499,5 @@ export default connect(mapStateToProps, {
createGroup,
updateGroup,
selectGroup,
setFilter,
})(withRouter(withTranslation()(SectionBodyContent)));

View File

@ -6,6 +6,7 @@ import { IconButton } from "asc-web-components";
import { Headline } from "asc-web-common";
import { withTranslation } from "react-i18next";
import { resetGroup } from "../../../../../store/group/actions";
import { setFilter } from "../../../../../store/people/actions";
import styled from "styled-components";
const Wrapper = styled.div`
@ -23,7 +24,6 @@ const Wrapper = styled.div`
`;
class SectionHeaderContent extends React.Component {
constructor(props) {
super(props);
const { group, t, groupCaption } = props;
@ -32,14 +32,14 @@ class SectionHeaderContent extends React.Component {
: t("CustomNewDepartment", { groupCaption });
this.state = {
headerText
}
headerText,
};
}
onClickBack = () => {
const { history, settings, resetGroup } = this.props;
const { filter, resetGroup, setFilter } = this.props;
resetGroup();
history.push(settings.homepage);
setFilter(filter);
};
render() {
@ -65,22 +65,22 @@ class SectionHeaderContent extends React.Component {
SectionHeaderContent.propTypes = {
group: PropTypes.object,
history: PropTypes.object.isRequired
history: PropTypes.object.isRequired,
};
SectionHeaderContent.defaultProps = {
group: null
group: null,
};
function mapStateToProps(state) {
return {
settings: state.auth.settings,
group: state.group.targetGroup,
groupCaption: state.auth.settings.customNames.groupCaption
groupCaption: state.auth.settings.customNames.groupCaption,
filter: state.people.filter,
};
}
export default connect(
mapStateToProps,
{ resetGroup }
)(withTranslation()(withRouter(SectionHeaderContent)));
export default connect(mapStateToProps, { resetGroup, setFilter })(
withTranslation()(withRouter(SectionHeaderContent))
);

View File

@ -12,7 +12,10 @@ import {
toEmployeeWrapper,
} from "../../../../../store/people/selectors";
import { withTranslation, Trans } from "react-i18next";
import { updateUserStatus } from "../../../../../store/people/actions";
import {
updateUserStatus,
setFilter,
} from "../../../../../store/people/actions";
import { updateProfile } from "../../../../../store/profile/actions";
import {
fetchProfile,
@ -405,9 +408,9 @@ class SectionHeaderContent extends React.PureComponent {
};
onClickBack = () => {
const { history, settings } = this.props;
history.push(settings.homepage);
const { filter, setFilter } = this.props;
setFilter(filter);
//history.push(settings.homepage);
};
render() {
@ -519,4 +522,5 @@ export default connect(mapStateToProps, {
updateUserStatus,
fetchProfile,
updateProfile,
setFilter,
})(withRouter(withTranslation()(SectionHeaderContent)));

View File

@ -24,7 +24,7 @@ const { isAdmin, isVisitor, getLanguage } = store.auth.selectors;
class PureProfile extends React.Component {
componentDidMount() {
const { match, fetchProfile, t, location } = this.props;
const { match, fetchProfile, profile, location, t } = this.props;
const { userId } = match.params;
setDocumentTitle(t("Profile"));
@ -39,8 +39,9 @@ class PureProfile extends React.Component {
if (linkParams.email_change && linkParams.email_change === "success") {
toastr.success(t("ChangeEmailSuccess"));
}
fetchProfile(userId);
if (!profile) {
fetchProfile(userId);
}
}
componentDidUpdate(prevProps) {

View File

@ -6,7 +6,7 @@ import {
Button,
Textarea,
AvatarEditor,
Text
Text,
} from "asc-web-components";
import { withTranslation, Trans } from "react-i18next";
import {
@ -16,13 +16,22 @@ import {
getUserContacts,
mapGroupsToGroupSelectorOptions,
mapGroupSelectorOptionsToGroups,
filterGroupSelectorOptions
filterGroupSelectorOptions,
} from "../../../../../store/people/selectors";
import { createProfile } from "../../../../../store/profile/actions";
import {
createProfile,
updateCreatedAvatar,
} from "../../../../../store/profile/actions";
import {
setFilter,
updateProfileInUsers,
setIsVisibleDataLossDialog,
setIsEditingForm,
} from "../../../../../store/people/actions";
import {
MainContainer,
AvatarContainer,
MainFieldsContainer
MainFieldsContainer,
} from "./FormFields/Form";
import TextField from "./FormFields/TextField";
import PasswordField from "./FormFields/PasswordField";
@ -32,6 +41,7 @@ import RadioField from "./FormFields/RadioField";
import DepartmentField from "./FormFields/DepartmentField";
import ContactsField from "./FormFields/ContactsField";
import InfoFieldContainer from "./FormFields/InfoFieldContainer";
import { DataLossWarningDialog } from "../../../../dialogs";
import { api, toastr } from "asc-web-common";
const { createThumbnailsAvatar, loadAvatar } = api.people;
@ -47,6 +57,7 @@ class CreateUserForm extends React.Component {
this.onBirthdayDateChange = this.onBirthdayDateChange.bind(this);
this.onWorkFromDateChange = this.onWorkFromDateChange.bind(this);
this.onCancel = this.onCancel.bind(this);
this.onCancelHandler = this.onCancelHandler.bind(this);
this.onContactsItemAdd = this.onContactsItemAdd.bind(this);
this.onContactsItemTypeChange = this.onContactsItemTypeChange.bind(this);
@ -74,15 +85,17 @@ class CreateUserForm extends React.Component {
y: this.state.avatar.y,
width: this.state.avatar.width,
height: this.state.avatar.height,
tmpFile: this.state.avatar.tmpFile
tmpFile: this.state.avatar.tmpFile,
})
.then(() => {
.then((res) => {
this.props.updateCreatedAvatar(res);
this.props.updateProfileInUsers();
toastr.success(this.props.t("ChangesSavedSuccessfully"));
this.props.history.push(
`${this.props.settings.homepage}/view/${userName}`
);
})
.catch(error => toastr.error(error));
.catch((error) => toastr.error(error));
}
openAvatarEditor() {
@ -100,37 +113,38 @@ class CreateUserForm extends React.Component {
tmpFile: this.state.avatar.tmpFile,
image: this.state.avatar.image,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2]
}
defaultHeight: avatarDefaultSizes[2],
},
});
}
this.setState({
visibleAvatarEditor: true
visibleAvatarEditor: true,
});
}
onLoadFileAvatar(file) {
onLoadFileAvatar(file, callback) {
let data = new FormData();
let _this = this;
data.append("file", file);
data.append("Autosave", false);
loadAvatar(0, data)
.then(response => {
.then((response) => {
var img = new Image();
img.onload = function() {
img.onload = function () {
var stateCopy = Object.assign({}, _this.state);
stateCopy.avatar = {
tmpFile: response.data,
image: response.data,
defaultWidth: img.width,
defaultHeight: img.height
defaultHeight: img.height,
};
_this.setState(stateCopy);
if (typeof callback === "function") callback();
};
img.src = response.data;
})
.catch(error => toastr.error(error));
.catch((error) => toastr.error(error));
}
onSaveAvatar(isUpdate, result, file) {
@ -149,6 +163,7 @@ class CreateUserForm extends React.Component {
stateCopy.avatar.height = result.height;
}
this.setState(stateCopy);
this.setIsEdit();
}
onCloseAvatarEditor() {
@ -156,8 +171,8 @@ class CreateUserForm extends React.Component {
visibleAvatarEditor: false,
croppedAvatarImage: "",
avatar: {
tmpFile: ""
}
tmpFile: "",
},
});
}
@ -167,10 +182,10 @@ class CreateUserForm extends React.Component {
}
}
mapPropsToState = props => {
mapPropsToState = (props) => {
var profile = toEmployeeWrapper({
isVisitor: props.match.params.type === "guest",
passwordType: "link"
passwordType: "link",
});
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
@ -182,14 +197,14 @@ class CreateUserForm extends React.Component {
firstName: false,
lastName: false,
email: false,
password: false
password: false,
},
profile: profile,
selector: {
visible: false,
allOptions: allOptions,
options: [...allOptions],
selected: selected
selected: selected,
},
avatar: {
tmpFile: "",
@ -199,27 +214,34 @@ class CreateUserForm extends React.Component {
x: 0,
y: 0,
width: 0,
height: 0
}
height: 0,
},
};
};
setIsEdit() {
const { editingForm, setIsEditingForm } = this.props;
if (!editingForm.isEdit) setIsEditingForm(true);
}
onInputChange(event) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile[event.target.name] = event.target.value;
this.setState(stateCopy);
this.setIsEdit();
}
onBirthdayDateChange(value) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.birthday = value ? value.toJSON() : null;
this.setState(stateCopy);
this.setIsEdit();
}
onWorkFromDateChange(value) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.workFrom = value ? value.toJSON() : null;
this.setState(stateCopy);
this.setIsEdit();
}
validate() {
@ -228,7 +250,7 @@ class CreateUserForm extends React.Component {
firstName: !profile.firstName.trim(),
lastName: !profile.lastName.trim(),
email: stateErrors.email || !profile.email.trim(),
password: profile.passwordType === "temp" && !profile.password.trim()
password: profile.passwordType === "temp" && !profile.password.trim(),
};
const hasError =
errors.firstName || errors.lastName || errors.email || errors.password;
@ -250,7 +272,7 @@ class CreateUserForm extends React.Component {
this.props
.createProfile(this.state.profile)
.then(profile => {
.then((profile) => {
if (this.state.avatar.tmpFile !== "") {
this.createAvatar(profile.id, profile.userName);
} else {
@ -260,14 +282,25 @@ class CreateUserForm extends React.Component {
);
}
})
.catch(error => {
.catch((error) => {
toastr.error(error);
this.setState({ isLoading: false });
});
}
onCancelHandler() {
const { editingForm, setIsVisibleDataLossDialog } = this.props;
if (editingForm.isEdit) {
setIsVisibleDataLossDialog(true);
} else {
this.onCancel();
}
}
onCancel() {
this.props.history.push(this.props.settings.homepage);
const { filter, setFilter } = this.props;
setFilter(filter);
}
onContactsItemAdd(item) {
@ -275,37 +308,41 @@ class CreateUserForm extends React.Component {
stateCopy.profile.contacts.push({
id: new Date().getTime().toString(),
type: item.value,
value: ""
value: "",
});
this.setState(stateCopy);
this.setIsEdit();
}
onContactsItemTypeChange(item) {
const id = item.key.split("_")[0];
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.contacts.forEach(element => {
stateCopy.profile.contacts.forEach((element) => {
if (element.id === id) element.type = item.value;
});
this.setState(stateCopy);
this.setIsEdit();
}
onContactsItemTextChange(event) {
const id = event.target.name.split("_")[0];
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.contacts.forEach(element => {
stateCopy.profile.contacts.forEach((element) => {
if (element.id === id) element.value = event.target.value;
});
this.setState(stateCopy);
this.setIsEdit();
}
onContactsItemRemove(event) {
const id = event.target.closest(".remove_icon").dataset.for.split("_")[0];
var stateCopy = Object.assign({}, this.state);
const filteredArray = stateCopy.profile.contacts.filter(element => {
const filteredArray = stateCopy.profile.contacts.filter((element) => {
return element.id !== id;
});
stateCopy.profile.contacts = filteredArray;
this.setState(stateCopy);
this.setIsEdit();
}
onShowGroupSelector() {
@ -335,20 +372,21 @@ class CreateUserForm extends React.Component {
stateCopy.selector.selected = selected;
stateCopy.selector.visible = false;
this.setState(stateCopy);
this.setIsEdit();
}
onRemoveGroup(id) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.groups = stateCopy.profile.groups.filter(
group => group.id !== id
(group) => group.id !== id
);
stateCopy.selector.selected = stateCopy.selector.selected.filter(
option => option.key !== id
(option) => option.key !== id
);
this.setState(stateCopy);
}
onValidateEmailField = value =>
onValidateEmailField = (value) =>
this.setState({ errors: { ...this.state.errors, email: !value.isValid } });
render() {
@ -357,7 +395,7 @@ class CreateUserForm extends React.Component {
const {
regDateCaption,
userPostCaption,
groupCaption
groupCaption,
} = settings.customNames;
const pattern = getUserContactsPattern();
@ -366,6 +404,7 @@ class CreateUserForm extends React.Component {
return (
<>
<MainContainer>
<DataLossWarningDialog onContinue={this.onCancel} />
<AvatarContainer>
<Avatar
size="max"
@ -447,7 +486,7 @@ class CreateUserForm extends React.Component {
radioValue={profile.passwordType}
radioOptions={[
{ value: "link", label: t("ActivationLink") },
{ value: "temp", label: t("TemporaryPassword") }
{ value: "temp", label: t("TemporaryPassword") },
]}
radioIsDisabled={isLoading}
radioOnChange={this.onInputChange}
@ -478,7 +517,7 @@ class CreateUserForm extends React.Component {
radioValue={profile.sex}
radioOptions={[
{ value: "male", label: t("MaleSexStatus") },
{ value: "female", label: t("FemaleSexStatus") }
{ value: "female", label: t("FemaleSexStatus") },
]}
radioIsDisabled={isLoading}
radioOnChange={this.onInputChange}
@ -571,7 +610,7 @@ class CreateUserForm extends React.Component {
/>
<Button
label={t("CancelButton")}
onClick={this.onCancel}
onClick={this.onCancelHandler}
isDisabled={isLoading}
size="big"
style={{ marginLeft: "8px" }}
@ -583,16 +622,22 @@ class CreateUserForm extends React.Component {
}
}
const mapStateToProps = state => {
const mapStateToProps = (state) => {
const { settings } = state.auth;
const { groups, filter, editingForm } = state.people;
return {
settings: state.auth.settings,
groups: state.people.groups
settings,
groups,
filter,
editingForm,
};
};
export default connect(
mapStateToProps,
{
createProfile
}
)(withRouter(withTranslation()(CreateUserForm)));
export default connect(mapStateToProps, {
createProfile,
updateCreatedAvatar,
setFilter,
updateProfileInUsers,
setIsVisibleDataLossDialog,
setIsEditingForm,
})(withRouter(withTranslation()(CreateUserForm)));

View File

@ -7,7 +7,7 @@ import {
Textarea,
Text,
AvatarEditor,
Link
Link,
} from "asc-web-components";
import { withTranslation, Trans } from "react-i18next";
import {
@ -17,17 +17,23 @@ import {
getUserContacts,
mapGroupsToGroupSelectorOptions,
mapGroupSelectorOptionsToGroups,
filterGroupSelectorOptions
filterGroupSelectorOptions,
} from "../../../../../store/people/selectors";
import {
updateProfile,
getUserPhoto,
fetchProfile
fetchProfile,
} from "../../../../../store/profile/actions";
import {
setFilter,
updateProfileInUsers,
setIsVisibleDataLossDialog,
setIsEditingForm,
} from "../../../../../store/people/actions";
import {
MainContainer,
AvatarContainer,
MainFieldsContainer
MainFieldsContainer,
} from "./FormFields/Form";
import TextField from "./FormFields/TextField";
import TextChangeField from "./FormFields/TextChangeField";
@ -37,18 +43,19 @@ import DepartmentField from "./FormFields/DepartmentField";
import ContactsField from "./FormFields/ContactsField";
import InfoFieldContainer from "./FormFields/InfoFieldContainer";
import styled from "styled-components";
import { DataLossWarningDialog } from "../../../../dialogs";
import { api, toastr } from "asc-web-common";
import {
ChangeEmailDialog,
ChangePasswordDialog,
ChangePhoneDialog
ChangePhoneDialog,
} from "../../../../dialogs";
const { createThumbnailsAvatar, loadAvatar, deleteAvatar } = api.people;
const dialogsDataset = {
changeEmail: "changeEmail",
changePassword: "changePassword",
changePhone: "changePhone"
changePhone: "changePhone",
};
const Table = styled.table`
@ -76,6 +83,7 @@ class UpdateUserForm extends React.Component {
this.onBirthdayDateChange = this.onBirthdayDateChange.bind(this);
this.onWorkFromDateChange = this.onWorkFromDateChange.bind(this);
this.onCancel = this.onCancel.bind(this);
this.onCancelHandler = this.onCancelHandler.bind(this);
this.onContactsItemAdd = this.onContactsItemAdd.bind(this);
this.onContactsItemTypeChange = this.onContactsItemTypeChange.bind(this);
@ -102,12 +110,12 @@ class UpdateUserForm extends React.Component {
}
}
mapPropsToState = props => {
mapPropsToState = (props) => {
var profile = toEmployeeWrapper(props.profile);
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
getUserPhoto(profile.id).then(userPhotoData => {
getUserPhoto(profile.id).then((userPhotoData) => {
if (userPhotoData.original) {
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
@ -120,8 +128,8 @@ class UpdateUserForm extends React.Component {
? userPhotoData.original.indexOf("default_user_photo") !== -1
? null
: userPhotoData.original
: null
}
: null,
},
});
}
}
@ -131,7 +139,7 @@ class UpdateUserForm extends React.Component {
isLoading: false,
errors: {
firstName: false,
lastName: false
lastName: false,
},
profile: profile,
visibleAvatarEditor: false,
@ -139,20 +147,20 @@ class UpdateUserForm extends React.Component {
visible: false,
allOptions: allOptions,
options: [...allOptions],
selected: selected
selected: selected,
},
avatar: {
tmpFile: "",
image: null,
defaultWidth: 0,
defaultHeight: 0
defaultHeight: 0,
},
dialogsVisible: {
[dialogsDataset.changePassword]: false,
[dialogsDataset.changePhone]: false,
[dialogsDataset.changeEmail]: false,
currentDialog: ""
}
currentDialog: "",
},
};
//Set unique contacts id
@ -165,13 +173,19 @@ class UpdateUserForm extends React.Component {
return newState;
};
setIsEdit() {
const { editingForm, setIsEditingForm } = this.props;
if (!editingForm.isEdit) setIsEditingForm(true);
}
onInputChange(event) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile[event.target.name] = event.target.value;
this.setState(stateCopy);
this.setIsEdit();
}
toggleDialogsVisible = e => {
toggleDialogsVisible = (e) => {
const stateCopy = Object.assign({}, {}, this.state.dialogsVisible);
const selectedDialog = e ? e.target.dataset.dialog : e;
if (selectedDialog) {
@ -188,25 +202,28 @@ class UpdateUserForm extends React.Component {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.isVisitor = event.target.value === "true";
this.setState(stateCopy);
this.setIsEdit();
}
onBirthdayDateChange(value) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.birthday = value ? value.toJSON() : null;
this.setState(stateCopy);
this.setIsEdit();
}
onWorkFromDateChange(value) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.workFrom = value ? value.toJSON() : null;
this.setState(stateCopy);
this.setIsEdit();
}
validate() {
const { profile } = this.state;
const errors = {
firstName: !profile.firstName.trim(),
lastName: !profile.lastName.trim()
lastName: !profile.lastName.trim(),
};
const hasError = errors.firstName || errors.lastName;
@ -227,20 +244,36 @@ class UpdateUserForm extends React.Component {
this.props
.updateProfile(this.state.profile)
.then(profile => {
.then((profile) => {
this.props.updateProfileInUsers(profile);
toastr.success(this.props.t("ChangesSavedSuccessfully"));
this.props.history.push(
`${this.props.settings.homepage}/view/${profile.userName}`
);
})
.catch(error => {
.catch((error) => {
toastr.error(error);
this.setState({ isLoading: false });
});
}
onCancelHandler() {
const { editingForm, setIsVisibleDataLossDialog } = this.props;
if (editingForm.isEdit) {
setIsVisibleDataLossDialog(true);
} else {
this.onCancel();
}
}
onCancel() {
this.props.history.goBack();
const { filter, setFilter } = this.props;
if (document.referrer) {
this.props.history.goBack();
} else {
setFilter(filter);
}
}
onContactsItemAdd(item) {
@ -248,37 +281,41 @@ class UpdateUserForm extends React.Component {
stateCopy.profile.contacts.push({
id: new Date().getTime().toString(),
type: item.value,
value: ""
value: "",
});
this.setState(stateCopy);
this.setIsEdit();
}
onContactsItemTypeChange(item) {
const id = item.key.split("_")[0];
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.contacts.forEach(element => {
stateCopy.profile.contacts.forEach((element) => {
if (element.id === id) element.type = item.value;
});
this.setState(stateCopy);
this.setIsEdit();
}
onContactsItemTextChange(event) {
const id = event.target.name.split("_")[0];
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.contacts.forEach(element => {
stateCopy.profile.contacts.forEach((element) => {
if (element.id === id) element.value = event.target.value;
});
this.setState(stateCopy);
this.setIsEdit();
}
onContactsItemRemove(event) {
const id = event.target.closest(".remove_icon").dataset.for.split("_")[0];
var stateCopy = Object.assign({}, this.state);
const filteredArray = stateCopy.profile.contacts.filter(element => {
const filteredArray = stateCopy.profile.contacts.filter((element) => {
return element.id !== id;
});
stateCopy.profile.contacts = filteredArray;
this.setState(stateCopy);
this.setIsEdit();
}
openAvatarEditor() {
@ -296,12 +333,12 @@ class UpdateUserForm extends React.Component {
tmpFile: this.state.avatar.tmpFile,
image: this.state.avatar.image,
defaultWidth: avatarDefaultSizes[1],
defaultHeight: avatarDefaultSizes[2]
}
defaultHeight: avatarDefaultSizes[2],
},
});
}
this.setState({
visibleAvatarEditor: true
visibleAvatarEditor: true,
});
}
@ -312,15 +349,15 @@ class UpdateUserForm extends React.Component {
data.append("file", file);
data.append("Autosave", false);
loadAvatar(this.state.profile.id, data)
.then(response => {
.then((response) => {
var img = new Image();
img.onload = function() {
img.onload = function () {
var stateCopy = Object.assign({}, _this.state);
stateCopy.avatar = {
tmpFile: response.data,
image: response.data,
defaultWidth: img.width,
defaultHeight: img.height
defaultHeight: img.height,
};
_this.setState(stateCopy);
_this.setState({ isLoading: false });
@ -328,7 +365,7 @@ class UpdateUserForm extends React.Component {
};
img.src = response.data;
})
.catch(error => {
.catch((error) => {
toastr.error(error);
this.setState({ isLoading: false });
});
@ -346,9 +383,9 @@ class UpdateUserForm extends React.Component {
),
width: result.width,
height: result.height,
tmpFile: this.state.avatar.tmpFile
tmpFile: this.state.avatar.tmpFile,
})
.then(response => {
.then((response) => {
let stateCopy = Object.assign({}, this.state);
stateCopy.visibleAvatarEditor = false;
stateCopy.avatar.tmpFile = "";
@ -356,32 +393,37 @@ class UpdateUserForm extends React.Component {
response.max +
"?_=" +
Math.floor(Math.random() * Math.floor(10000));
toastr.success(this.props.t("ChangesSavedSuccessfully"));
this.setState({ isLoading: false });
this.setState(stateCopy);
this.setIsEdit();
})
.catch(error => {
.catch((error) => {
toastr.error(error);
this.setState({ isLoading: false });
})
.then(() => this.props.updateProfile(this.props.profile))
.then(() => this.props.fetchProfile(this.state.profile.id));
.then(() => {
this.props.updateProfile(this.props.profile);
this.setState({ isLoading: false });
});
} else {
deleteAvatar(this.state.profile.id)
.then(response => {
.then((response) => {
let stateCopy = Object.assign({}, this.state);
stateCopy.visibleAvatarEditor = false;
stateCopy.profile.avatarMax = response.big;
toastr.success(this.props.t("ChangesSavedSuccessfully"));
this.setState(stateCopy);
this.setIsEdit();
})
.catch(error => toastr.error(error));
.catch((error) => toastr.error(error));
}
}
onCloseAvatarEditor() {
this.setState({
visibleAvatarEditor: false
visibleAvatarEditor: false,
});
}
@ -412,17 +454,19 @@ class UpdateUserForm extends React.Component {
stateCopy.selector.selected = selected;
stateCopy.selector.visible = false;
this.setState(stateCopy);
this.setIsEdit();
}
onRemoveGroup(id) {
var stateCopy = Object.assign({}, this.state);
stateCopy.profile.groups = stateCopy.profile.groups.filter(
group => group.id !== id
(group) => group.id !== id
);
stateCopy.selector.selected = stateCopy.selector.selected.filter(
option => option.key !== id
(option) => option.key !== id
);
this.setState(stateCopy);
this.setIsEdit();
}
render() {
@ -433,7 +477,7 @@ class UpdateUserForm extends React.Component {
userCaption,
regDateCaption,
userPostCaption,
groupCaption
groupCaption,
} = settings.customNames;
const pattern = getUserContactsPattern();
@ -517,6 +561,7 @@ class UpdateUserForm extends React.Component {
return (
<>
<MainContainer>
<DataLossWarningDialog onContinue={this.onCancel} />
<AvatarContainer>
<Avatar
size="max"
@ -562,7 +607,8 @@ class UpdateUserForm extends React.Component {
case of loss of the password and send notifications.
<p
style={{
margin: "1rem 0" /*, height: "0", visibility: "hidden"*/
margin:
"1rem 0" /*, height: "0", visibility: "hidden"*/,
}}
>
You can create a new mail on the domain as the primary. In
@ -636,7 +682,7 @@ class UpdateUserForm extends React.Component {
radioValue={profile.sex}
radioOptions={[
{ value: "male", label: t("MaleSexStatus") },
{ value: "female", label: t("FemaleSexStatus") }
{ value: "female", label: t("FemaleSexStatus") },
]}
radioIsDisabled={isLoading}
radioOnChange={this.onInputChange}
@ -647,7 +693,7 @@ class UpdateUserForm extends React.Component {
radioValue={profile.isVisitor.toString()}
radioOptions={[
{ value: "true", label: guestCaption },
{ value: "false", label: userCaption }
{ value: "false", label: userCaption },
]}
radioIsDisabled={isLoading}
radioOnChange={this.onUserTypeChange}
@ -742,7 +788,7 @@ class UpdateUserForm extends React.Component {
/>
<Button
label={t("CancelButton")}
onClick={this.onCancel}
onClick={this.onCancelHandler}
isDisabled={isLoading}
size="big"
style={{ marginLeft: "8px" }}
@ -778,18 +824,21 @@ class UpdateUserForm extends React.Component {
}
}
const mapStateToProps = state => {
const mapStateToProps = (state) => {
return {
profile: state.profile.targetUser,
settings: state.auth.settings,
groups: state.people.groups
groups: state.people.groups,
editingForm: state.people.editingForm,
filter: state.people.filter,
};
};
export default connect(
mapStateToProps,
{
updateProfile,
fetchProfile
}
)(withRouter(withTranslation()(UpdateUserForm)));
export default connect(mapStateToProps, {
updateProfile,
fetchProfile,
updateProfileInUsers,
setIsVisibleDataLossDialog,
setIsEditingForm,
setFilter,
})(withRouter(withTranslation()(UpdateUserForm)));

View File

@ -1,10 +1,14 @@
import React, { useCallback } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import React, { useCallback } from "react";
import styled from "styled-components";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import { IconButton } from 'asc-web-components';
import { Headline } from 'asc-web-common';
import { useTranslation } from 'react-i18next';
import { IconButton } from "asc-web-components";
import { Headline } from "asc-web-common";
import { useTranslation } from "react-i18next";
import {
setFilter,
setIsVisibleDataLossDialog,
} from "../../../../../store/people/actions";
const Wrapper = styled.div`
display: flex;
@ -18,45 +22,56 @@ const Wrapper = styled.div`
}
.header-headline {
margin-left: 16px;
}
margin-left: 16px;
}
`;
const SectionHeaderContent = (props) => {
const { profile, history, match, settings } = props;
const {
profile,
history,
match,
settings,
filter,
editingForm,
setFilter,
setIsVisibleDataLossDialog,
} = props;
const { userCaption, guestCaption } = settings.customNames;
const { type } = match.params;
const { t } = useTranslation();
const headerText = type
? type === "guest"
? t('CustomCreation', { user: guestCaption })
: t('CustomCreation', { user: userCaption })
? t("CustomCreation", { user: guestCaption })
: t("CustomCreation", { user: userCaption })
: profile
? `${t('EditUserDialogTitle')} (${profile.displayName})`
: "";
? `${t("EditUserDialogTitle")} (${profile.displayName})`
: "";
const onClickBackHandler = () => {
if (editingForm.isEdit) {
setIsVisibleDataLossDialog(true, onClickBack);
} else {
onClickBack();
}
};
const onClickBack = useCallback(() => {
//history.goBack();
!profile ? history.push(settings.homepage) : history.push(`/products/people/view/${profile.userName}`);
}, [history, profile, settings.homepage]);
!profile || !document.referrer ? setFilter(filter) : history.goBack();
}, [history, profile, setFilter, filter]);
return (
<Wrapper>
<IconButton
iconName='ArrowPathIcon'
iconName="ArrowPathIcon"
color="#A3A9AE"
size="17"
hoverColor="#657077"
isFill={true}
onClick={onClickBack}
onClick={onClickBackHandler}
className="arrow-button"
/>
<Headline
className='header-headline'
type='content'
truncate={true}
>
<Headline className="header-headline" type="content" truncate={true}>
{headerText}
</Headline>
</Wrapper>
@ -66,8 +81,13 @@ const SectionHeaderContent = (props) => {
function mapStateToProps(state) {
return {
profile: state.profile.targetUser,
settings: state.auth.settings
settings: state.auth.settings,
filter: state.people.filter,
editingForm: state.people.editingForm,
};
};
}
export default connect(mapStateToProps)(withRouter(SectionHeaderContent));
export default connect(mapStateToProps, {
setFilter,
setIsVisibleDataLossDialog,
})(withRouter(SectionHeaderContent));

View File

@ -14,6 +14,7 @@ import {
UpdateUserForm,
} from "./Section";
import { fetchProfile } from "../../../store/profile/actions";
import { setIsEditingForm } from "../../../store/people/actions";
import { I18nextProvider, withTranslation } from "react-i18next";
import { createI18N } from "../../../helpers/i18n";
import { setDocumentTitle } from "../../../helpers/utils";
@ -27,12 +28,14 @@ const { isAdmin } = store.auth.selectors;
class ProfileAction extends React.Component {
componentDidMount() {
const { match, fetchProfile, t } = this.props;
const { match, fetchProfile, isEdit, setIsEditingForm, t } = this.props;
const { userId } = match.params;
setDocumentTitle(t("ProfileAction"));
changeLanguage(i18n);
if (isEdit) {
setIsEditingForm(false);
}
if (userId) {
fetchProfile(userId);
}
@ -129,9 +132,10 @@ function mapStateToProps(state) {
isVisitor: state.auth.user.isVisitor,
profile: state.profile.targetUser,
isAdmin: isAdmin(state),
isEdit: state.people.editingForm.isEdit,
};
}
export default connect(mapStateToProps, { fetchProfile })(
export default connect(mapStateToProps, { fetchProfile, setIsEditingForm })(
ProfileActionContainer
);

View File

@ -9,8 +9,10 @@ import {
SORT_BY,
SORT_ORDER,
PAGE,
PAGE_COUNT
PAGE_COUNT,
} from "../../helpers/constants";
import { getUserByUserName } from "../people/selectors";
const { EmployeeStatus } = constants;
const { Filter } = api;
@ -24,39 +26,42 @@ export const SET_SELECTED = "SET_SELECTED";
export const SET_FILTER = "SET_FILTER";
export const SELECT_GROUP = "SELECT_GROUP";
export const SET_SELECTOR_USERS = "SET_SELECTOR_USERS";
export const SET_IS_VISIBLE_DATA_LOSS_DIALOG =
"SET_IS_VISIBLE_DATA_LOSS_DIALOG";
export const SET_IS_EDITING_FORM = "SET_IS_EDITING_FORM";
export function setUser(user) {
return {
type: SET_USER,
user
user,
};
}
export function setUsers(users) {
return {
type: SET_USERS,
users
users,
};
}
export function setGroups(groups) {
return {
type: SET_GROUPS,
groups
groups,
};
}
export function setSelection(selection) {
return {
type: SET_SELECTION,
selection
selection,
};
}
export function setSelected(selected) {
return {
type: SET_SELECTED,
selected
selected,
};
}
@ -75,14 +80,14 @@ export function selectGroup(groupId) {
export function selectUser(user) {
return {
type: SELECT_USER,
user
user,
};
}
export function deselectUser(user) {
return {
type: DESELECT_USER,
user
user,
};
}
@ -120,7 +125,7 @@ export function setFilterUrl(filter) {
//const isProfileView = history.location.pathname.includes('/people/view') || history.location.pathname.includes('/people/edit');
//if (params.length > 0 && !isProfileView) {
history.push(`${config.homepage}/filter?${params.join("&")}`);
history.push(`${config.homepage}/filter?${params.join("&")}`);
//}
}
@ -128,20 +133,35 @@ export function setFilter(filter) {
setFilterUrl(filter);
return {
type: SET_FILTER,
filter
filter,
};
}
export function setSelectorUsers(users) {
return {
type: SET_SELECTOR_USERS,
users
users,
};
}
export function setIsVisibleDataLossDialog(isVisible, callback) {
return {
type: SET_IS_VISIBLE_DATA_LOSS_DIALOG,
isVisible,
callback,
};
}
export function setIsEditingForm(isEdit) {
return {
type: SET_IS_EDITING_FORM,
isEdit,
};
}
export function fetchSelectorUsers() {
return dispatch => {
api.people.getSelectorUserList().then(data => {
return (dispatch) => {
api.people.getSelectorUserList().then((data) => {
const users = data.items;
return dispatch(setSelectorUsers(users));
});
@ -149,10 +169,10 @@ export function fetchSelectorUsers() {
}
export function fetchGroups(dispatchFunc = null) {
return api.groups.getGroupList().then(groups => {
return api.groups.getGroupList().then((groups) => {
return dispatchFunc
? dispatchFunc(setGroups(groups))
: Promise.resolve(dispatch => dispatch(setGroups(groups)));
: Promise.resolve((dispatch) => dispatch(setGroups(groups)));
});
}
@ -160,21 +180,33 @@ export function fetchPeople(filter, dispatchFunc = null) {
return dispatchFunc
? fetchPeopleByFilter(dispatchFunc, filter)
: (dispatch, getState) => {
if (filter) {
return fetchPeopleByFilter(dispatch, filter);
} else {
const { people } = getState();
const { filter } = people;
return fetchPeopleByFilter(dispatch, filter);
}
};
if (filter) {
return fetchPeopleByFilter(dispatch, filter);
} else {
const { people } = getState();
const { filter } = people;
return fetchPeopleByFilter(dispatch, filter);
}
};
}
export function removeUser(userId, filter) {
return dispatch => {
return api.people.deleteUsers(userId)
.then(() => fetchPeople(filter, dispatch))
return (dispatch) => {
return api.people
.deleteUsers(userId)
.then(() => fetchPeople(filter, dispatch));
};
}
export function updateUserList(dispatch, filter) {
let filterData = filter && filter.clone();
if (!filterData) {
filterData = Filter.getDefault();
filterData.employeeStatus = EmployeeStatus.Active;
}
return api.people.getUserList(filterData).then((data) => {
return dispatch(setUsers(data.items));
});
}
function fetchPeopleByFilter(dispatch, filter) {
@ -185,12 +217,12 @@ function fetchPeopleByFilter(dispatch, filter) {
filterData.employeeStatus = EmployeeStatus.Active;
}
return api.people.getUserList(filterData).then(data => {
return api.people.getUserList(filterData).then((data) => {
filterData.total = data.total;
dispatch(setFilter(filterData));
dispatch({
type: SELECT_GROUP,
groupId: filterData.group
groupId: filterData.group,
});
return dispatch(setUsers(data.items));
});
@ -198,21 +230,20 @@ function fetchPeopleByFilter(dispatch, filter) {
export function updateUserStatus(status, userIds, isRefetchPeople = false) {
return (dispatch, getState) => {
return api.people.updateUserStatus(status, userIds)
.then(users => {
const { people } = getState();
const { filter } = people;
return isRefetchPeople
? fetchPeople(filter, dispatch)
: Promise.resolve();
});
return api.people.updateUserStatus(status, userIds).then((users) => {
const { people } = getState();
const { filter } = people;
return isRefetchPeople
? fetchPeople(filter, dispatch)
: Promise.resolve();
});
};
}
export function updateUserType(type, userIds) {
return dispatch => {
return api.people.updateUserType(type, userIds).then(users => {
users.forEach(user => {
return (dispatch) => {
return api.people.updateUserType(type, userIds).then((users) => {
users.forEach((user) => {
dispatch(setUser(user));
});
});
@ -229,3 +260,43 @@ export function resetFilter() {
return fetchPeople(newFilter, dispatch);
};
}
export function updateProfileInUsers(updatedProfile) {
return (dispatch, getState) => {
const { people } = getState();
const { users } = people;
if (!users) {
return updateUserList(dispatch);
}
if (!updatedProfile) {
const { profile } = getState();
updatedProfile = profile.targetUser;
}
const { userName } = updatedProfile;
const oldProfile = getUserByUserName(users, userName);
const newProfile = {};
for (let key in oldProfile) {
if (
updatedProfile.hasOwnProperty(key) &&
updatedProfile[key] !== oldProfile[key]
) {
newProfile[key] = updatedProfile[key];
} else {
newProfile[key] = oldProfile[key];
}
}
const updatedUsers = users.map((user) => {
if (user.id === newProfile.id) {
return newProfile;
} else {
return user;
}
});
return dispatch(setUsers(updatedUsers));
};
}

View File

@ -8,7 +8,9 @@ import {
SET_FILTER,
SELECT_GROUP,
SET_USER,
SET_SELECTOR_USERS
SET_SELECTOR_USERS,
SET_IS_VISIBLE_DATA_LOSS_DIALOG,
SET_IS_EDITING_FORM,
} from "./actions";
import { isUserSelected, skipUser, getUsersBySelected } from "./selectors";
import { api } from "asc-web-common";
@ -22,60 +24,79 @@ const initialState = {
selectedGroup: null,
filter: Filter.getDefault(),
selector: {
users: []
}
users: [],
},
editingForm: {
isEdit: false,
isVisibleDataLossDialog: false,
},
};
const peopleReducer = (state = initialState, action) => {
switch (action.type) {
case SET_GROUPS:
return Object.assign({}, state, {
groups: action.groups
groups: action.groups,
});
case SET_USERS:
return Object.assign({}, state, {
users: action.users
users: action.users,
});
case SET_USER:
return Object.assign({}, state, {
users: state.users.map(user =>
users: state.users.map((user) =>
user.id === action.user.id ? action.user : user
)
),
});
case SET_SELECTION:
return Object.assign({}, state, {
selection: action.selection
selection: action.selection,
});
case SELECT_USER:
if (!isUserSelected(state.selection, action.user.id)) {
return Object.assign({}, state, {
selection: [...state.selection, action.user]
selection: [...state.selection, action.user],
});
} else return state;
case DESELECT_USER:
if (isUserSelected(state.selection, action.user.id)) {
return Object.assign({}, state, {
selection: skipUser(state.selection, action.user.id)
selection: skipUser(state.selection, action.user.id),
});
} else return state;
case SET_SELECTED:
return Object.assign({}, state, {
selected: action.selected,
selection: getUsersBySelected(state.users, action.selected)
selection: getUsersBySelected(state.users, action.selected),
});
case SET_FILTER:
return Object.assign({}, state, {
filter: action.filter
filter: action.filter,
});
case SELECT_GROUP:
return Object.assign({}, state, {
selectedGroup: action.groupId
selectedGroup: action.groupId,
});
case SET_SELECTOR_USERS:
return Object.assign({}, state, {
selector: Object.assign({}, state.selector, {
users: action.users
})
users: action.users,
}),
});
case SET_IS_VISIBLE_DATA_LOSS_DIALOG:
return Object.assign({}, state, {
editingForm: {
...state.editingForm,
isVisibleDataLossDialog: action.isVisible,
callback: action.callback,
},
});
case SET_IS_EDITING_FORM:
return Object.assign({}, state, {
editingForm: {
...state.editingForm,
isEdit: action.isEdit,
},
});
default:
return state;

View File

@ -1,5 +1,4 @@
import { getUserByUserName } from "../people/selectors";
import { fetchPeople } from "../people/actions";
import { updateUserList } from "../people/actions";
import { store, api } from "asc-web-common";
const { setCurrentUser } = store.auth.actions;
const { isMe } = store.auth.selectors;
@ -43,7 +42,6 @@ export function fetchProfile(userName) {
}
};
}
export function createProfile(profile) {
return (dispatch, getState) => {
const { people } = getState();
@ -58,7 +56,7 @@ export function createProfile(profile) {
return dispatch(setProfile(user));
})
.then(() => {
return fetchPeople(filter, dispatch);
return updateUserList(dispatch, filter);
})
.then(() => {
return Promise.resolve(result);
@ -67,9 +65,7 @@ export function createProfile(profile) {
}
export function updateProfile(profile) {
return (dispatch, getState) => {
const { people } = getState();
const { filter } = people;
return (dispatch) => {
const member = employeeWrapperToMemberModel(profile);
let result;
@ -79,15 +75,11 @@ export function updateProfile(profile) {
result = user;
return Promise.resolve(dispatch(setProfile(user)));
})
.then(() => {
return fetchPeople(filter, dispatch);
})
.then(() => {
return Promise.resolve(result);
});
};
}
export function updateProfileCulture(id, culture) {
return (dispatch) => {
return api.people.updateUserCulture(id, culture).then((user) => {
@ -100,3 +92,18 @@ export function updateProfileCulture(id, culture) {
export function getUserPhoto(id) {
return api.people.getUserPhoto(id);
}
export function updateCreatedAvatar(avatar) {
return (dispatch, getState) => {
const { big, max, medium, small } = avatar;
const { profile } = getState();
const newProfile = {
...profile.targetUser,
avatarMax: max,
avatarMedium: medium,
avatar: big,
avatarSmall: small,
};
return dispatch(setProfile(newProfile));
};
}

View File

@ -117,7 +117,7 @@ class Confirm extends React.PureComponent {
const loginData = {
userName: this.state.email,
password: hash,
passwordHash: hash,
};
const personalData = {

View File

@ -10,11 +10,16 @@ import {
PasswordInput,
Loader,
toastr,
Heading
Heading,
} from "asc-web-components";
import { PageLayout } from "asc-web-common";
import { store } from "asc-web-common";
import { getConfirmationInfo, changePassword } from '../../../../store/confirm/actions';
import { store, utils as commonUtils } from "asc-web-common";
import {
getConfirmationInfo,
changePassword,
} from "../../../../store/confirm/actions";
const { createPasswordHash } = commonUtils;
const { logout } = store.auth.actions;
const BodyStyle = styled.form`
@ -55,27 +60,27 @@ class Form extends React.PureComponent {
isLoading: false,
passwordEmpty: false,
key: linkData.confirmHeader,
userId: linkData.uid
userId: linkData.uid,
};
}
onKeyPress = target => {
onKeyPress = (target) => {
if (target.key === "Enter") {
this.onSubmit();
}
};
onChange = event => {
onChange = (event) => {
this.setState({ password: event.target.value });
!this.state.passwordValid && this.setState({ passwordValid: true });
event.target.value.trim() && this.setState({ passwordEmpty: false });
this.onKeyPress(event);
};
onSubmit = e => {
onSubmit = (e) => {
this.setState({ isLoading: true }, function () {
const { userId, password, key } = this.state;
const { history, changePassword } = this.props;
const { history, changePassword, hashSettings } = this.props;
let hasError = false;
if (!this.state.passwordValid) {
@ -89,14 +94,15 @@ class Form extends React.PureComponent {
this.setState({ isLoading: false });
return false;
}
const hash = createPasswordHash(password, hashSettings);
changePassword(userId, password, key)
changePassword(userId, hash, key)
.then(() => this.props.logout())
.then(() => {
history.push("/");
toastr.success(this.props.t("ChangePasswordSuccess"));
})
.catch(error => {
.catch((error) => {
toastr.error(this.props.t(`${error}`));
this.setState({ isLoading: false });
});
@ -105,7 +111,7 @@ class Form extends React.PureComponent {
componentDidMount() {
const { getConfirmationInfo, history } = this.props;
getConfirmationInfo(this.state.key).catch(error => {
getConfirmationInfo(this.state.key).catch((error) => {
toastr.error(this.props.t(`${error}`));
history.push("/");
});
@ -119,72 +125,72 @@ class Form extends React.PureComponent {
window.removeEventListener("keyup", this.onKeyPress);
}
validatePassword = value => this.setState({ passwordValid: value });
validatePassword = (value) => this.setState({ passwordValid: value });
render() {
const { settings, isConfirmLoaded, t, greetingTitle } = this.props;
const { isLoading, password, passwordEmpty } = this.state;
return !isConfirmLoaded ? (
<Loader className="pageLoader" type="rombs" size='40px' />
<Loader className="pageLoader" type="rombs" size="40px" />
) : (
<BodyStyle>
<div className="password-header">
<img
className="password-logo"
src="images/dark_general.png"
alt="Logo"
/>
<Heading className="password-title" color="#116d9d">
{greetingTitle}
</Heading>
</div>
<Text className="password-text" fontSize='14px'>
{t("PassworResetTitle")}
</Text>
<PasswordInput
id="password"
name="password"
inputName="password"
inputValue={password}
size="huge"
scale={true}
type="password"
isDisabled={isLoading}
hasError={passwordEmpty}
onValidateInput={this.validatePassword}
generatorSpecial="!@#$%^&*"
tabIndex={1}
value={password}
onChange={this.onChange}
emailInputName="E-mail"
passwordSettings={settings}
tooltipPasswordTitle="Password must contain:"
tooltipPasswordLength={`${t("ErrorPasswordLength", {
fromNumber: settings.minLength,
toNumber: 30
})}:`}
placeholder={t("PasswordCustomMode")}
maxLength={30}
onKeyDown={this.onKeyPress}
isAutoFocussed={true}
inputWidth="490px"
<BodyStyle>
<div className="password-header">
<img
className="password-logo"
src="images/dark_general.png"
alt="Logo"
/>
<Button
id="button"
className="password-button"
primary
size="big"
tabIndex={2}
label={
isLoading ? t("LoadingProcessing") : t("ImportContactsOkButton")
}
isDisabled={isLoading}
isLoading={isLoading}
onClick={this.onSubmit}
/>
</BodyStyle>
);
<Heading className="password-title" color="#116d9d">
{greetingTitle}
</Heading>
</div>
<Text className="password-text" fontSize="14px">
{t("PassworResetTitle")}
</Text>
<PasswordInput
id="password"
name="password"
inputName="password"
inputValue={password}
size="huge"
scale={true}
type="password"
isDisabled={isLoading}
hasError={passwordEmpty}
onValidateInput={this.validatePassword}
generatorSpecial="!@#$%^&*"
tabIndex={1}
value={password}
onChange={this.onChange}
emailInputName="E-mail"
passwordSettings={settings}
tooltipPasswordTitle="Password must contain:"
tooltipPasswordLength={`${t("ErrorPasswordLength", {
fromNumber: settings.minLength,
toNumber: 30,
})}:`}
placeholder={t("PasswordCustomMode")}
maxLength={30}
onKeyDown={this.onKeyPress}
isAutoFocussed={true}
inputWidth="490px"
/>
<Button
id="button"
className="password-button"
primary
size="big"
tabIndex={2}
label={
isLoading ? t("LoadingProcessing") : t("ImportContactsOkButton")
}
isDisabled={isLoading}
isLoading={isLoading}
onClick={this.onSubmit}
/>
</BodyStyle>
);
}
}
@ -192,14 +198,14 @@ Form.propTypes = {
history: PropTypes.object.isRequired,
changePassword: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
linkData: PropTypes.object.isRequired
linkData: PropTypes.object.isRequired,
};
Form.defaultProps = {
password: ""
password: "",
};
const ChangePasswordForm = props => (
const ChangePasswordForm = (props) => (
<PageLayout>
<PageLayout.SectionBody>
<Form {...props} />
@ -213,12 +219,13 @@ function mapStateToProps(state) {
isConfirmLoaded: state.confirm.isConfirmLoaded,
settings: state.auth.settings.passwordSettings,
isAuthenticated: state.auth.isAuthenticated,
greetingTitle: state.auth.settings.greetingSettings
greetingTitle: state.auth.settings.greetingSettings,
hashSettings: state.auth.settings.hashSettings,
};
}
export default connect(mapStateToProps, {
changePassword,
getConfirmationInfo,
logout
logout,
})(withRouter(withTranslation()(ChangePasswordForm)));

View File

@ -127,7 +127,7 @@ class Confirm extends React.PureComponent {
const loginData = {
userName: this.state.email,
password: hash,
passwordHash: hash,
};
const personalData = {

View File

@ -31,7 +31,7 @@ export function createConfirmUser(registerData, loginData, key) {
return api.people
.createUser(data, key)
.then((user) => dispatch(setCurrentUser(user)))
.then(() => api.user.login(loginData.userName, loginData.password))
.then(() => api.user.login(loginData.userName, loginData.passwordHash))
.then(() => loadInitInfo(dispatch));
};
}
@ -51,12 +51,12 @@ export function activateConfirmUser(
return (dispatch) => {
return api.people
.changePassword(userId, loginData.password, key)
.changePassword(userId, loginData.passwordHash, key)
.then((data) => {
return api.people.updateActivationStatus(activationStatus, userId, key);
})
.then((data) => {
return dispatch(login(loginData.userName, loginData.password));
return dispatch(login(loginData.userName, loginData.passwordHash));
})
.then((data) => {
return api.people.updateUser(changedData);
@ -73,10 +73,10 @@ export function changeEmail(userId, email, key) {
};
}
export function changePassword(userId, password, key) {
export function changePassword(userId, passwordHash, key) {
return (dispatch) => {
return api.people
.changePassword(userId, password, key)
.changePassword(userId, passwordHash, key)
.then(() => logout(dispatch));
};
}

View File

@ -1,6 +1,6 @@
{
"name": "asc-web-common",
"version": "1.0.248",
"version": "1.0.249",
"description": "Ascensio System SIA common components and solutions library",
"license": "AGPL-3.0",
"files": [

View File

@ -1,234 +1,232 @@
import { request } from "../client";
//import axios from "axios";
import Filter from "./filter";
import * as fakePeople from "./fake";
export function getUserList(filter = Filter.getDefault(), fake = false) {
if(fake) {
return fakePeople.getUserList(filter);
}
const params =
filter && filter instanceof Filter
? `/filter.json?${filter.toUrlParams()}`
: "";
return request({
method: "get",
url: `/people${params}`
});
if (fake) {
return fakePeople.getUserList(filter);
}
export function getUser(userName = null) {
return request({
method: "get",
url: `/people/${userName || '@self'}.json`
});
}
export function getUserPhoto(userId) {
return request({
method: "get",
url: `/people/${userId}/photo`
});
}
const params =
filter && filter instanceof Filter
? `/filter.json?${filter.toUrlParams()}`
: "";
export function createUser(data, confirmKey = null) {
const options = {
method: "post",
url: "/people",
data: data
};
return request({
method: "get",
url: `/people${params}`
});
}
if(confirmKey)
options.headers = { confirm: confirmKey };
export function getUser(userName = null) {
return request({
method: "get",
url: `/people/${userName || "@self"}.json`
});
}
export function getUserPhoto(userId) {
return request({
method: "get",
url: `/people/${userId}/photo`
});
}
return request(options);
}
export function createUser(data, confirmKey = null) {
const options = {
method: "post",
url: "/people",
data: data
};
export function changePassword(userId, password, key) {
const data = { password };
if (confirmKey) options.headers = { confirm: confirmKey };
return request({
method: "put",
url: `/people/${userId}/password`,
data,
headers: { confirm: key }
});
}
return request(options);
}
export function changeEmail(userId, email, key) {
const data = { email };
export function changePassword(userId, passwordHash, key) {
const data = { passwordHash };
return request({
method: "put",
url: `/people/${userId}/password`,
data,
headers: { confirm: key }
});
}
export function updateActivationStatus(activationStatus, userId, key) {
return request({
method: "put",
url: `/people/activationstatus/${activationStatus}.json`,
data: { userIds: [userId] },
headers: { confirm: key }
});
}
return request({
method: "put",
url: `/people/${userId}/password`,
data,
headers: { confirm: key }
});
}
export function updateUser(data) {
return request({
method: "put",
url: `/people/${data.id}`,
data
});
}
export function changeEmail(userId, email, key) {
const data = { email };
export function deleteSelf(key) {
return request({
method: "delete",
url: "/people/@self",
headers: { confirm: key }
});
}
return request({
method: "put",
url: `/people/${userId}/password`,
data,
headers: { confirm: key }
});
}
export function updateActivationStatus(activationStatus, userId, key) {
return request({
method: "put",
url: `/people/activationstatus/${activationStatus}.json`,
data: { userIds: [userId] },
headers: { confirm: key }
});
}
export function sendInstructionsToChangePassword(email) {
return request({
method: "post",
url: "/people/password.json",
data: { email }
});
}
export function updateUser(data) {
return request({
method: "put",
url: `/people/${data.id}`,
data
});
}
export function getListAdmins(filter = Filter.getDefault()) {
const filterParams = filter.toUrlParams();
const params =
export function deleteSelf(key) {
return request({
method: "delete",
url: "/people/@self",
headers: { confirm: key }
});
}
export function sendInstructionsToChangePassword(email) {
return request({
method: "post",
url: "/people/password.json",
data: { email }
});
}
export function getListAdmins(filter = Filter.getDefault()) {
const filterParams = filter.toUrlParams();
const params =
"fields=id,displayName,groups,name,avatar,avatarSmall,isOwner,isAdmin,profileUrl,listAdminModules";
return request({
method: "get",
url: `/people/filter.json?${filterParams}&${params}`
});
}
export function getAdmins(isParams) {
let params = "&fields";
if (isParams) {
params =
"fields=id,displayName,groups,name,avatar,avatarSmall,isOwner,isAdmin,profileUrl,listAdminModules";
return request({
method: "get",
url: `/people/filter.json?${filterParams}&${params}`
});
}
return request({
method: "get",
url: `/people/filter.json?isadministrator=true&${params}`
});
}
export function getAdmins(isParams) {
let params = "&fields";
if (isParams) {
params =
"fields=id,displayName,groups,name,avatar,avatarSmall,isOwner,isAdmin,profileUrl,listAdminModules";
export function changeProductAdmin(userId, productId, administrator) {
return request({
method: "put",
url: "/settings/security/administrator",
data: {
productId,
userId,
administrator
}
return request({
method: "get",
url: `/people/filter.json?isadministrator=true&${params}`
});
}
});
}
export function changeProductAdmin(userId, productId, administrator) {
return request({
method: "put",
url: "/settings/security/administrator",
data: {
productId,
userId,
administrator
}
});
}
export function getUserById(userId) {
return request({
method: "get",
url: `/people/${userId}`
});
}
export function getUserById(userId) {
return request({
method: "get",
url: `/people/${userId}`
});
}
export function resendUserInvites(userIds) {
return request({
method: "put",
url: "/people/invite",
data: { userIds }
});
}
export function resendUserInvites(userIds) {
return request({
method: "put",
url: "/people/invite",
data: { userIds }
});
}
export function updateUserCulture(id, cultureName) {
return request({
method: "put",
url: `/people/${id}/culture`,
data: { cultureName }
});
}
export function updateUserCulture(id, cultureName) {
return request({
method: "put",
url: `/people/${id}/culture`,
data: { cultureName }
});
}
export function loadAvatar(profileId, data) {
return request({
method: "post",
url: `/people/${profileId}/photo`,
data
});
}
export function loadAvatar(profileId, data) {
return request({
method: "post",
url: `/people/${profileId}/photo`,
data
});
}
export function createThumbnailsAvatar(profileId, data) {
return request({
method: "post",
url: `/people/${profileId}/photo/thumbnails.json`,
data
});
}
export function createThumbnailsAvatar(profileId, data) {
return request({
method: "post",
url: `/people/${profileId}/photo/thumbnails.json`,
data
});
}
export function deleteAvatar(profileId) {
return request({
method: "delete",
url: `/people/${profileId}/photo`
});
}
export function deleteAvatar(profileId) {
return request({
method: "delete",
url: `/people/${profileId}/photo`
});
}
export function updateUserStatus(status, userIds) {
return request({
method: "put",
url: `/people/status/${status}`,
data: { userIds }
});
}
export function updateUserStatus(status, userIds) {
return request({
method: "put",
url: `/people/status/${status}`,
data: { userIds }
});
}
export function updateUserType(type, userIds) {
return request({
method: "put",
url: `/people/type/${type}`,
data: { userIds }
});
}
export function updateUserType(type, userIds) {
return request({
method: "put",
url: `/people/type/${type}`,
data: { userIds }
});
}
export function sendInstructionsToDelete() {
return request({
method: "put",
url: "/people/self/delete.json"
});
}
export function sendInstructionsToDelete() {
return request({
method: "put",
url: "/people/self/delete.json"
});
}
export function sendInstructionsToChangeEmail(userId, email) {
return request({
method: "post",
url: "/people/email.json",
data: { userId, email }
});
}
export function sendInstructionsToChangeEmail(userId, email) {
return request({
method: "post",
url: "/people/email.json",
data: { userId, email }
});
}
export function deleteUser(userId) {
return request({
method: "delete",
url: `/people/${userId}.json`
});
}
export function deleteUser(userId) {
return request({
method: "delete",
url: `/people/${userId}.json`
});
}
export function deleteUsers(userIds) {
return request({
method: "put",
url: "/people/delete.json",
data: { userIds }
});
}
export function deleteUsers(userIds) {
return request({
method: "put",
url: "/people/delete.json",
data: { userIds }
});
}
export function getSelectorUserList() {
return request({
method: "get",
url: "/people/filter.json?fields=id,displayName,groups"
});
}
export function getSelectorUserList() {
return request({
method: "get",
url: "/people/filter.json?fields=id,displayName,groups"
});
}

View File

@ -3,7 +3,7 @@ import styled from "styled-components";
const StyledMain = styled.main`
height: calc(100vh - 56px);
/*height: calc(var(--vh, 1vh) * 100 - 56px);*/
height: calc(var(--vh, 1vh) * 100 - 56px);
width: 100vw;
z-index: 0;
display: flex;