Web: People: Simplified People Home page (fixed render issues)

This commit is contained in:
Alexey Safronov 2021-03-02 15:11:17 +03:00
parent c304bbe8b7
commit 3ce638b69a
8 changed files with 457 additions and 391 deletions

View File

@ -1,3 +1,4 @@
//import "./wdyr";
import React from "react";
import Shell from "studio/shell";

View File

@ -5,12 +5,14 @@ import PropTypes from "prop-types";
import Button from "@appserver/components/button";
import ModalDialog from "@appserver/components/modal-dialog";
import Text from "@appserver/components/text";
import history from "@appserver/common/history";
import { withTranslation, Trans } from "react-i18next";
import api from "@appserver/common/api";
import toastr from "@appserver/common/components/Toast";
import ModalDialogContainer from "../ModalDialogContainer";
import { inject, observer } from "mobx-react";
const { deleteUser } = api.people; //TODO: Move to action
@ -38,8 +40,8 @@ class DeleteProfileEverDialogComponent extends React.Component {
};
onReassignDataClick = () => {
const { history, settings, user } = this.props;
history.push(`${settings.homepage}/reassign/${user.userName}`);
const { homepage, user } = this.props;
history.push(`${homepage}/reassign/${user.userName}`);
};
render() {
@ -101,11 +103,11 @@ DeleteProfileEverDialog.propTypes = {
user: PropTypes.object.isRequired,
filter: PropTypes.instanceOf(Filter).isRequired,
fetchPeople: PropTypes.func.isRequired,
settings: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
export default inject(({ auth, peopleStore }) => ({
homepage: auth.settingsStore.homepage,
userCaption: auth.settingsStore.customNames.userCaption,
fetchPeople: peopleStore.usersStore.getUsersList,
filter: peopleStore.filterStore.filter,
}))(observer(withRouter(DeleteProfileEverDialog)));

View File

@ -0,0 +1,60 @@
import { inject, observer } from "mobx-react";
import React from "react";
import {
ChangeEmailDialog,
ChangePasswordDialog,
DeleteSelfProfileDialog,
DeleteProfileEverDialog,
} from "../../../../dialogs";
const Dialogs = ({
changeEmail,
changePassword,
deleteSelfProfile,
deleteProfileEver,
data,
closeDialogs,
filter,
}) => {
return (
<>
{changeEmail && (
<ChangeEmailDialog
visible={changeEmail}
onClose={closeDialogs}
user={data}
/>
)}
{changePassword && (
<ChangePasswordDialog
visible={changePassword}
onClose={closeDialogs}
email={data.email}
/>
)}
{deleteSelfProfile && (
<DeleteSelfProfileDialog
visible={deleteSelfProfile}
onClose={closeDialogs}
email={data.email}
/>
)}
{deleteProfileEver && (
<DeleteProfileEverDialog
visible={deleteProfileEver}
onClose={closeDialogs}
user={data}
/>
)}
</>
);
};
export default inject(({ peopleStore }) => ({
changeEmail: peopleStore.dialogStore.changeEmail,
changePassword: peopleStore.dialogStore.changePassword,
deleteSelfProfile: peopleStore.dialogStore.deleteSelfProfile,
deleteProfileEver: peopleStore.dialogStore.deleteProfileEver,
data: peopleStore.dialogStore.data,
closeDialogs: peopleStore.dialogStore.closeDialogs,
}))(observer(Dialogs));

View File

@ -0,0 +1,315 @@
import React from "react";
import Row from "@appserver/components/row";
import Avatar from "@appserver/components/avatar";
import UserContent from "./userContent";
import history from "@appserver/common/history";
import { inject, observer } from "mobx-react";
import { Trans, useTranslation } from "react-i18next";
import toastr from "@appserver/common/components/Toast/toastr";
import { EmployeeStatus } from "@appserver/common/constants";
import { resendUserInvites } from "@appserver/common/api/people"; //TODO: Move to store action
const SimpleUserRow = ({
man,
sectionWidth,
checked,
isAdmin,
isMobile,
selectUser,
selectGroup,
deselectUser,
homepage,
setChangeEmailDialogVisible,
setDeleteProfileDialogVisible,
setDeleteSelfProfileDialogVisible,
setDialogData,
closeDialogs,
updateUserStatus,
}) => {
const { t } = useTranslation("Home");
const isRefetchPeople = true;
const {
//checked,
role,
displayName,
avatar,
id,
status,
options,
} = man;
const onContentRowSelect = (checked, user) => {
if (checked) {
selectUser(user);
} else {
deselectUser(user);
}
};
const onEmailSentClick = (e) => {
window.open("mailto:" + man.email);
};
const onSendMessageClick = (e) => {
window.open(`sms:${man.mobilePhone}`);
};
const onEditClick = (e) => {
history.push(`${homepage}/edit/${man.userName}`);
};
const toggleChangeEmailDialog = (e) => {
const { id, email } = man;
setDialogData({
email,
id,
});
setChangeEmailDialogVisible(true);
};
const toggleChangePasswordDialog = (e) => {
const { email } = man;
setDialogData({
email,
});
setChangePasswordDialogVisible(true);
};
const toggleDeleteSelfProfileDialog = (e) => {
closeDialogs();
const { email } = man;
setDialogData({
email,
});
setDeleteSelfProfileDialogVisible(true);
};
const toggleDeleteProfileEverDialog = (e) => {
closeDialogs();
const { id, displayName, userName } = man;
setDialogData({
id,
displayName,
userName,
});
setDeleteProfileDialogVisible(true);
};
const onDisableClick = (e) => {
//onLoading(true);
updateUserStatus(EmployeeStatus.Disabled, [man.id], isRefetchPeople)
.then(() => toastr.success(t("SuccessChangeUserStatus")))
.catch((error) => toastr.error(error));
//.finally(() => onLoading(false));
};
const onEnableClick = (e) => {
//onLoading(true);
updateUserStatus(EmployeeStatus.Active, [man.id], isRefetchPeople)
.then(() => toastr.success(t("SuccessChangeUserStatus")))
.catch((error) => toastr.error(error));
//.finally(() => onLoading(false));
};
const onReassignDataClick = (e) => {
history.push(`${homepage}/reassign/${man.userName}`);
};
const onDeletePersonalDataClick = (e) => {
//const user = this.findUserById(e.currentTarget.dataset.id);
toastr.success("Context action: Delete personal data"); //TODO: Implement and add translation
};
const onInviteAgainClick = (e) => {
//onLoading(true);
resendUserInvites([man.id])
.then(() =>
toastr.success(
<Trans
i18nKey="MessageEmailActivationInstuctionsSentOnEmail"
ns="Home"
>
The email activation instructions have been sent to the
<strong>{{ email: man.email }}</strong> email address
</Trans>
)
)
.catch((error) => toastr.error(error));
//.finally(() => onLoading(false));
};
const getUserContextOptions = (options, id) => {
return options.map((option) => {
switch (option) {
case "send-email":
return {
key: option,
label: t("LblSendEmail"),
"data-id": id,
onClick: onEmailSentClick,
};
case "send-message":
return {
key: option,
label: t("LblSendMessage"),
"data-id": id,
onClick: onSendMessageClick,
};
case "separator":
return { key: option, isSeparator: true };
case "edit":
return {
key: option,
label: t("EditButton"),
"data-id": id,
onClick: onEditClick,
};
case "change-password":
return {
key: option,
label: t("PasswordChangeButton"),
"data-id": id,
onClick: toggleChangePasswordDialog,
};
case "change-email":
return {
key: option,
label: t("EmailChangeButton"),
"data-id": id,
onClick: toggleChangeEmailDialog,
};
case "delete-self-profile":
return {
key: option,
label: t("DeleteSelfProfile"),
"data-id": id,
onClick: toggleDeleteSelfProfileDialog,
};
case "disable":
return {
key: option,
label: t("DisableUserButton"),
"data-id": id,
onClick: onDisableClick,
};
case "enable":
return {
key: option,
label: t("EnableUserButton"),
"data-id": id,
onClick: onEnableClick,
};
case "reassign-data":
return {
key: option,
label: t("ReassignData"),
"data-id": id,
onClick: onReassignDataClick,
};
case "delete-personal-data":
return {
key: option,
label: t("RemoveData"),
"data-id": id,
onClick: onDeletePersonalDataClick,
};
case "delete-profile":
return {
key: option,
label: t("DeleteSelfProfile"),
"data-id": id,
onClick: toggleDeleteProfileEverDialog,
};
case "invite-again":
return {
key: option,
label: t("LblInviteAgain"),
"data-id": id,
onClick: onInviteAgainClick,
};
default:
break;
}
return undefined;
});
};
const showContextMenu = options && options.length > 0;
const checkedProps = isAdmin ? { checked } : {};
const element = (
<Avatar size="min" role={role} userName={displayName} source={avatar} />
);
const contextOptionsProps =
(isAdmin && showContextMenu) || (showContextMenu && id === currentUserId)
? {
contextOptions: getUserContextOptions(options, id),
}
: {};
return (
<Row
key={id}
status={status}
data={man}
element={element}
onSelect={onContentRowSelect}
{...checkedProps}
{...contextOptionsProps}
//needForUpdate={this.needForUpdate}
sectionWidth={sectionWidth}
>
<UserContent
isMobile={isMobile}
//widthProp={widthProp}
user={man}
history={history}
selectGroup={selectGroup}
sectionWidth={sectionWidth}
/>
</Row>
);
};
export default inject(({ auth, peopleStore }, { man }) => {
return {
homepage: auth.settingsStore.homepage,
isAdmin: auth.isAdmin,
checked: peopleStore.selectionStore.selection.some(
(el) => el.id === man.id
),
selectUser: peopleStore.selectionStore.selectUser,
deselectUser: peopleStore.selectionStore.deselectUser,
selectGroup: peopleStore.selectedGroupStore.selectGroup,
setChangeEmailDialogVisible:
peopleStore.dialogStore.setChangeEmailDialogVisible,
setChangePasswordDialogVisible:
peopleStore.dialogStore.setChangePasswordDialogVisible,
setDeleteSelfProfileDialogVisible:
peopleStore.dialogStore.setDeleteSelfProfileDialogVisible,
setDeleteProfileDialogVisible:
peopleStore.dialogStore.setDeleteProfileDialogVisible,
setDialogData: peopleStore.dialogStore.setDialogData,
closeDialogs: peopleStore.dialogStore.closeDialogs,
updateUserStatus: peopleStore.usersStore.updateUserStatus,
};
})(observer(SimpleUserRow));

View File

@ -1,44 +1,28 @@
import React from "react";
import { withRouter } from "react-router";
import { withTranslation, Trans } from "react-i18next";
import styled from "styled-components";
//import styled from "styled-components";
import Row from "@appserver/components/row";
import Avatar from "@appserver/components/avatar";
import RowContainer from "@appserver/components/row-container";
import { Consumer } from "@appserver/components/utils/context";
import { isArrayEqual } from "@appserver/components/utils/array";
//import { isArrayEqual } from "@appserver/components/utils/array";
import UserContent from "./userContent";
import equal from "fast-deep-equal/react";
import { resendUserInvites } from "@appserver/common/api/people";
import toastr from "@appserver/common/components/Toast";
import { EmployeeStatus } from "@appserver/common/constants";
//import equal from "fast-deep-equal/react";
import toastr from "@appserver/common/components/Toast/toastr";
//import { EmployeeStatus } from "@appserver/common/constants";
import Loaders from "@appserver/common/components/Loaders";
import {
ChangeEmailDialog,
ChangePasswordDialog,
DeleteSelfProfileDialog,
DeleteProfileEverDialog,
} from "../../../../dialogs";
import EmptyScreen from "./sub-components/EmptyScreen";
import { inject, observer } from "mobx-react";
const isRefetchPeople = true;
import SimpleUserRow from "./SimpleUserRow";
import Dialogs from "./Dialogs";
class SectionBodyContent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
dialogsVisible: {
changeEmail: false,
changePassword: false,
deleteSelfProfile: false,
deleteProfileEver: false,
},
isEmailValid: false,
isLoadedSection: true,
};
}
@ -56,263 +40,7 @@ class SectionBodyContent extends React.PureComponent {
.finally(() => this.setState({ isLoadedSection: isLoaded }));
}
findUserById = (id) => this.props.peopleList.find((man) => man.id === id);
onEmailSentClick = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
window.open("mailto:" + user.email);
};
onSendMessageClick = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
window.open(`sms:${user.mobilePhone}`);
};
onEditClick = (e) => {
const { history, settings } = this.props;
const user = this.findUserById(e.currentTarget.dataset.id);
history.push(`${settings.homepage}/edit/${user.userName}`);
};
onDisableClick = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
const { updateUserStatus, onLoading, t } = this.props;
onLoading(true);
updateUserStatus(EmployeeStatus.Disabled, [user.id], isRefetchPeople)
.then(() => toastr.success(t("SuccessChangeUserStatus")))
.catch((error) => toastr.error(error))
.finally(() => onLoading(false));
};
onEnableClick = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
const { updateUserStatus, onLoading, t } = this.props;
onLoading(true);
updateUserStatus(EmployeeStatus.Active, [user.id], isRefetchPeople)
.then(() => toastr.success(t("SuccessChangeUserStatus")))
.catch((error) => toastr.error(error))
.finally(() => onLoading(false));
};
onReassignDataClick = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
const { history, settings } = this.props;
history.push(`${settings.homepage}/reassign/${user.userName}`);
};
onDeletePersonalDataClick = (e) => {
//const user = this.findUserById(e.currentTarget.dataset.id);
toastr.success("Context action: Delete personal data");
};
onCloseDialog = () => {
this.setState({
dialogsVisible: {
changeEmail: false,
changePassword: false,
deleteSelfProfile: false,
deleteProfileEver: false,
},
});
};
toggleChangeEmailDialog = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
if (!user) return;
const { id, email } = user;
this.setState({
dialogsVisible: {
changeEmail: true,
},
user: {
email,
id,
},
});
};
toggleChangePasswordDialog = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
if (!user) return;
const { email } = user;
this.setState({
dialogsVisible: {
changePassword: true,
},
user: { email },
});
};
toggleDeleteSelfProfileDialog = (e) => {
this.onCloseDialog();
const user = this.findUserById(e.currentTarget.dataset.id);
if (!user) return;
const { email } = user;
this.setState({
dialogsVisible: {
deleteSelfProfile: true,
},
user: { email },
});
};
toggleDeleteProfileEverDialog = (e) => {
this.onCloseDialog();
const user = this.findUserById(e.currentTarget.dataset.id);
if (!user) return;
const { id, displayName, userName } = user;
this.setState({
dialogsVisible: {
deleteProfileEver: true,
},
user: {
id,
displayName,
userName,
},
});
};
onInviteAgainClick = (e) => {
const user = this.findUserById(e.currentTarget.dataset.id);
const { onLoading } = this.props;
onLoading(true);
resendUserInvites([user.id])
.then(() =>
toastr.success(
<Trans
i18nKey="MessageEmailActivationInstuctionsSentOnEmail"
ns="Home"
>
The email activation instructions have been sent to the
<strong>{{ email: user.email }}</strong> email address
</Trans>
)
)
.catch((error) => toastr.error(error))
.finally(() => onLoading(false));
};
getUserContextOptions = (options, id) => {
const { t } = this.props;
return options.map((option) => {
switch (option) {
case "send-email":
return {
key: option,
label: t("LblSendEmail"),
"data-id": id,
onClick: this.onEmailSentClick,
};
case "send-message":
return {
key: option,
label: t("LblSendMessage"),
"data-id": id,
onClick: this.onSendMessageClick,
};
case "separator":
return { key: option, isSeparator: true };
case "edit":
return {
key: option,
label: t("EditButton"),
"data-id": id,
onClick: this.onEditClick,
};
case "change-password":
return {
key: option,
label: t("PasswordChangeButton"),
"data-id": id,
onClick: this.toggleChangePasswordDialog,
};
case "change-email":
return {
key: option,
label: t("EmailChangeButton"),
"data-id": id,
onClick: this.toggleChangeEmailDialog,
};
case "delete-self-profile":
return {
key: option,
label: t("DeleteSelfProfile"),
"data-id": id,
onClick: this.toggleDeleteSelfProfileDialog,
};
case "disable":
return {
key: option,
label: t("DisableUserButton"),
"data-id": id,
onClick: this.onDisableClick,
};
case "enable":
return {
key: option,
label: t("EnableUserButton"),
"data-id": id,
onClick: this.onEnableClick,
};
case "reassign-data":
return {
key: option,
label: t("ReassignData"),
"data-id": id,
onClick: this.onReassignDataClick,
};
case "delete-personal-data":
return {
key: option,
label: t("RemoveData"),
"data-id": id,
onClick: this.onDeletePersonalDataClick,
};
case "delete-profile":
return {
key: option,
label: t("DeleteSelfProfile"),
"data-id": id,
onClick: this.toggleDeleteProfileEverDialog,
};
case "invite-again":
return {
key: option,
label: t("LblInviteAgain"),
"data-id": id,
onClick: this.onInviteAgainClick,
};
default:
break;
}
return undefined;
});
};
onContentRowSelect = (checked, user) => {
if (checked) {
this.props.selectUser(user);
} else {
this.props.deselectUser(user);
}
};
//findUserById = (id) => this.props.peopleList.find((man) => man.id === id);
onResetFilter = () => {
const { onLoading, resetFilter } = this.props;
@ -320,7 +48,7 @@ class SectionBodyContent extends React.PureComponent {
resetFilter(true).finally(() => onLoading(false));
};
needForUpdate = (currentProps, nextProps) => {
/*needForUpdate = (currentProps, nextProps) => {
if (currentProps.checked !== nextProps.checked) {
return true;
}
@ -337,27 +65,27 @@ class SectionBodyContent extends React.PureComponent {
return true;
}
return false;
};
};*/
render() {
// console.log("Home SectionBodyContent render()");
const {
isLoaded,
peopleList,
history,
settings,
//history,
t,
filter,
//filter,
//widthProp,
isMobile,
selectGroup,
//selectGroup,
isLoading,
isAdmin,
currentUserId,
//isAdmin,
//currentUserId,
isEmptyGroup,
//isUserSelected,
} = this.props;
const { dialogsVisible, user, isLoadedSection } = this.state;
const { isLoadedSection } = this.state;
return !isLoaded || (isMobile && isLoading) || !isLoadedSection ? (
<Loaders.Rows isRectangle={false} />
@ -369,99 +97,17 @@ class SectionBodyContent extends React.PureComponent {
className="people-row-container"
useReactWindow={false}
>
{peopleList.map((man) => {
const {
checked,
role,
displayName,
avatar,
id,
status,
options,
} = man;
const sectionWidth = context.sectionWidth;
const showContextMenu = options && options.length > 0;
const contextOptionsProps =
(isAdmin && showContextMenu) ||
(showContextMenu && id === currentUserId)
? {
contextOptions: this.getUserContextOptions(options, id),
}
: {};
const checkedProps =
checked !== null && isAdmin ? { checked } : {};
const element = (
<Avatar
size="min"
role={role}
userName={displayName}
source={avatar}
/>
);
return (
<Row
key={id}
status={status}
data={man}
element={element}
onSelect={this.onContentRowSelect}
{...checkedProps}
{...contextOptionsProps}
needForUpdate={this.needForUpdate}
sectionWidth={sectionWidth}
>
<UserContent
isMobile={isMobile}
//widthProp={widthProp}
user={man}
history={history}
settings={settings}
selectGroup={selectGroup}
sectionWidth={sectionWidth}
/>
</Row>
);
})}
{peopleList.map((man) => (
<SimpleUserRow
man={man}
sectionWidth={context.sectionWidth}
isMobile={isMobile}
/>
))}
</RowContainer>
)}
</Consumer>
{dialogsVisible.changeEmail && (
<ChangeEmailDialog
visible={dialogsVisible.changeEmail}
onClose={this.onCloseDialog}
user={user}
/>
)}
{dialogsVisible.changePassword && (
<ChangePasswordDialog
visible={dialogsVisible.changePassword}
onClose={this.onCloseDialog}
email={user.email}
/>
)}
{dialogsVisible.deleteSelfProfile && (
<DeleteSelfProfileDialog
visible={dialogsVisible.deleteSelfProfile}
onClose={this.onCloseDialog}
email={user.email}
/>
)}
{dialogsVisible.deleteProfileEver && (
<DeleteProfileEverDialog
visible={dialogsVisible.deleteProfileEver}
onClose={this.onCloseDialog}
user={user}
filter={filter}
settings={settings}
history={history}
/>
)}
<Dialogs />
</>
) : (
<EmptyScreen
@ -474,18 +120,15 @@ class SectionBodyContent extends React.PureComponent {
}
export default inject(({ auth, peopleStore }) => ({
settings: auth.settingsStore,
isLoaded: auth.isLoaded,
isAdmin: auth.isAdmin,
//isAdmin: auth.isAdmin,
currentUserId: auth.userStore.user.id,
fetchPeople: peopleStore.usersStore.getUsersList,
peopleList: peopleStore.usersStore.peopleList,
filter: peopleStore.filterStore.filter,
resetFilter: peopleStore.resetFilter,
selectUser: peopleStore.selectionStore.selectUser,
deselectUser: peopleStore.selectionStore.deselectUser,
selectGroup: peopleStore.selectedGroupStore.selectGroup,
updateUserStatus: peopleStore.usersStore.updateUserStatus,
isLoading: peopleStore.isLoading,
isEmptyGroup: peopleStore.selectedGroupStore.isEmptyGroup,
}))(observer(withRouter(withTranslation("Home")(SectionBodyContent))));

View File

@ -0,0 +1,43 @@
import { makeAutoObservable } from "mobx";
class DialogStore {
changeEmail = false;
changePassword = false;
deleteSelfProfile = false;
deleteProfileEver = false;
data = {};
constructor() {
makeAutoObservable(this);
}
setChangeEmailDialogVisible = (visible) => {
this.changeEmail = visible;
};
setChangePasswordDialogVisible = (visible) => {
this.changePassword = visible;
};
setDeleteSelfProfileDialogVisible = (visible) => {
this.deleteSelfProfile = visible;
};
setDeleteProfileDialogVisible = (visible) => {
this.deleteProfileEver = visible;
};
setDialogData = (data) => {
this.data = data;
};
closeDialogs = () => {
this.setChangeEmailDialogVisible(false);
this.setChangePasswordDialogVisible(false);
this.setDeleteSelfProfileDialogVisible(false);
this.setDeleteProfileDialogVisible(false);
this.setDialogData({});
};
}
export default DialogStore;

View File

@ -12,6 +12,7 @@ import HeaderMenuStore from "./HeaderMenuStore";
import AvatarEditorStore from "./AvatarEditorStore";
import InviteLinksStore from "./InviteLinksStore";
import store from "studio/store";
import DialogStore from "./DialogStore";
const { auth: authStore } = store;
class PeopleStore {
@ -25,6 +26,7 @@ class PeopleStore {
headerMenuStore = null;
avatarEditorStore = null;
inviteLinksStore = null;
dialogStore = null;
isLoading = false;
isLoaded = false;
@ -40,6 +42,7 @@ class PeopleStore {
this.headerMenuStore = new HeaderMenuStore(this);
this.avatarEditorStore = new AvatarEditorStore(this);
this.inviteLinksStore = new InviteLinksStore(this);
this.dialogStore = new DialogStore();
makeObservable(this, {
isLoading: observable,

View File

@ -235,7 +235,6 @@ class UsersStore {
return {
id,
checked: isViewerAdmin ? this.isUserSelected(user.id) : undefined,
status,
activationStatus,
statusType,