Web: People: added send invite dialog to group users

This commit is contained in:
Nikita Gopienko 2020-02-20 09:26:51 +03:00
parent 05954f21e0
commit 7ada48f0bc
9 changed files with 291 additions and 76 deletions

View File

@ -40,8 +40,8 @@ const ModalDialogContainer = styled.div`
position: relative;
}
.delete-group-users_dialog {
.heading-toggle-content {
.toggle-content-dialog {
.heading-toggle-content {
font-size: 16px;
}
}

View File

@ -0,0 +1,54 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
import { constants } from 'asc-web-common';
const { LANGUAGE } = constants;
const newInstance = i18n.createInstance();
if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
react: {
useSuspense: true
},
backend: {
loadPath: `${config.homepage}/locales/DeleteProfileEverDialog/{{lng}}/{{ns}}.json`
}
});
} else if (process.env.NODE_ENV === "development") {
const resources = {
en: {
translation: require("./locales/en/translation.json")
},
ru: {
translation: require("./locales/ru/translation.json")
},
};
newInstance.init({
resources: resources,
lng: localStorage.getItem(LANGUAGE) || 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
react: {
useSuspense: true
}
});
}
export default newInstance;

View File

@ -0,0 +1,92 @@
import React, { useCallback, useState } from "react";
import { withRouter } from "react-router";
import PropTypes from "prop-types";
import {
toastr,
ModalDialog,
Button,
Text,
ToggleContent
} from "asc-web-components";
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
import { api, utils } from "asc-web-common";
import ModalDialogContainer from "../ModalDialogContainer";
const { resendUserInvites } = api.people;
const { changeLanguage } = utils;
const SendInviteDialogComponent = props => {
const { t, onClose, visible } = props;
const users = props.users.filter(x => x.status === 2);
const usersId = [];
users.map(item => usersId.push(item.id));
const [isRequestRunning, setIsRequestRunning] = useState(false);
changeLanguage(i18n);
const onSendInvite = useCallback(() => {
setIsRequestRunning(true);
resendUserInvites(usersId)
.then(() => toastr.success(t("SuccessSendInvitation")))
.catch(error => toastr.error(error))
.finally(() => setIsRequestRunning(false));
}, [t, usersId]);
//console.log("SendInviteDialog render");
return (
<ModalDialogContainer>
<ModalDialog
visible={visible}
onClose={onClose}
headerContent={t("SendInviteAgain")}
bodyContent={
<>
<Text>{t("SendInviteAgainDialog")}</Text>
<ToggleContent
className="toggle-content-dialog"
label={t("DeleteGroupUsersShowUsers")}
>
{users.map((item, index) => (
<Text key={index}>{item.displayName}</Text>
))}
</ToggleContent>
</>
}
footerContent={
<>
<Button
label={t("OKButton")}
size="medium"
primary
onClick={onSendInvite}
isLoading={isRequestRunning}
/>
<Button
className="button-dialog"
label={t("CancelButton")}
size="medium"
onClick={onClose}
isDisabled={isRequestRunning}
/>
</>
}
/>
</ModalDialogContainer>
);
};
const SendInviteDialogTranslated = withTranslation()(SendInviteDialogComponent);
const SendInviteDialog = props => (
<SendInviteDialogTranslated i18n={i18n} {...props} />
);
SendInviteDialog.propTypes = {
visible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
users: PropTypes.arrayOf(PropTypes.object).isRequired
};
export default withRouter(SendInviteDialog);

View File

@ -0,0 +1,8 @@
{
"OKButton": "OK",
"CancelButton": "Cancel",
"SendInviteAgain": "Send invitation once again",
"DeleteGroupUsersShowUsers": "View users list",
"SendInviteAgainDialog": "The invitation to the portal will be sent once again to the selected users with the 'Pending' status who are not disabled. After the users confirm the invitation to the portal their status will change to 'Active'"
}

View File

@ -0,0 +1,8 @@
{
"OKButton": "ОК",
"CancelButton": "Отмена",
"SendInviteAgain": "Отправить приглашение еще раз",
"DeleteGroupUsersShowUsers": "Показать список пользователей",
"SendInviteAgainDialog": "Приглашение на портал будет отправлено еще раз выбранным пользователям со статусом 'Ожидание', которые не заблокированы. После того, как пользователи подтвердят приглашение на портал, их статус изменится на 'Активный'"
}

View File

@ -4,7 +4,8 @@ import ChangePhoneDialog from "./ChangePhoneDialog";
import DeleteProfileEverDialog from "./DeleteProfileEverDialog";
import DeleteSelfProfileDialog from "./DeleteSelfProfileDialog";
import DeleteGroupUsersDialog from "./DeleteGroupUsersDialog";
import InviteDialog from './InviteDialog';
import InviteDialog from "./InviteDialog";
import SendInviteDialog from "./SendInviteDialog";
export {
ChangeEmailDialog,
@ -13,5 +14,6 @@ export {
DeleteProfileEverDialog,
DeleteSelfProfileDialog,
DeleteGroupUsersDialog,
InviteDialog
};
InviteDialog,
SendInviteDialog
};

View File

@ -7,13 +7,13 @@ import {
toastr,
ContextMenuButton
} from "asc-web-components";
import { Headline } from 'asc-web-common';
import { Headline } from "asc-web-common";
import { connect } from "react-redux";
import {
getSelectedGroup,
getSelectionIds,
getSelectionIds,
getUserType,
getGuestType,
getGuestType,
getUsersStatus,
getInactiveUsers,
getDeleteUsers
@ -26,29 +26,39 @@ import {
removeUser
} from "../../../../../store/people/actions";
import { deleteGroup } from "../../../../../store/group/actions";
import { store, api, constants } from 'asc-web-common';
import { InviteDialog, DeleteGroupUsersDialog } from '../../../../dialogs';
import { store, constants } from "asc-web-common";
import {
InviteDialog,
DeleteGroupUsersDialog,
SendInviteDialog
} from "../../../../dialogs";
const { isAdmin } = store.auth.selectors;
const { resendUserInvites } = api.people;
const { EmployeeStatus, EmployeeType } = constants;
const isRefetchPeople = true;
const StyledContainer = styled.div`
@media (min-width: 1024px) {
${props => props.isHeaderVisible && css`width: calc(100% + 76px);`}
${props =>
props.isHeaderVisible &&
css`
width: calc(100% + 76px);
`}
}
.group-button-menu-container {
margin: 0 -16px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
padding-bottom: 56px;
@media (max-width: 1024px) {
& > div:first-child {
${props => props.isArticlePinned && css`width: calc(100% - 240px);`}
${props =>
props.isArticlePinned &&
css`
width: calc(100% - 240px);
`}
position: absolute;
top: 56px;
z-index: 180;
@ -85,6 +95,7 @@ const StyledContainer = styled.div`
const SectionHeaderContent = props => {
const [dialogVisible, setDialogVisible] = useState(false);
const [deleteDialog, setDeleteDialog] = useState(false);
const [sendInviteDialog, setSendInviteDialog] = useState(false);
const {
isHeaderVisible,
@ -142,16 +153,14 @@ const SectionHeaderContent = props => {
toastr.success(t("SuccessChangeUserType"));
}, [selectedUserIds, updateUserType, t]);
const onSentInviteAgain = useCallback(() => {
resendUserInvites(selectedUserIds)
.then(() => toastr.success(t('SuccessSendInvitation')))
.catch(error => toastr.error(error));
}, [selectedUserIds, t]);
const onDelete = useCallback(() =>
setDeleteDialog(!deleteDialog), [deleteDialog]
const onSentInviteAgain = useCallback(
() => setSendInviteDialog(!sendInviteDialog),
[sendInviteDialog]
);
const onDelete = useCallback(() => setDeleteDialog(!deleteDialog), [
deleteDialog
]);
const menuItems = [
{
@ -168,12 +177,16 @@ const SectionHeaderContent = props => {
onSelect: item => onSelect(item.key)
},
{
label: t('ChangeToUser', { userCaption: settings.customNames.userCaption }),
label: t("ChangeToUser", {
userCaption: settings.customNames.userCaption
}),
disabled: userType,
onClick: onSetEmployee
},
{
label: t('ChangeToGuest', { guestCaption: settings.customNames.guestCaption }),
label: t("ChangeToGuest", {
guestCaption: settings.customNames.guestCaption
}),
disabled: guestType,
onClick: onSetGuest
},
@ -242,8 +255,9 @@ const SectionHeaderContent = props => {
history.push(`${settings.homepage}/group/create`);
}, [history, settings]);
const onInvitationDialogClick = useCallback(() =>
setDialogVisible(!dialogVisible), [dialogVisible]
const onInvitationDialogClick = useCallback(
() => setDialogVisible(!dialogVisible),
[dialogVisible]
);
const getContextOptionsPlus = useCallback(() => {
@ -264,34 +278,49 @@ const SectionHeaderContent = props => {
label: groupCaption,
onClick: goToGroupCreate
},
{ key: 'separator', isSeparator: true },
{ key: "separator", isSeparator: true },
{
key: "make-invitation-link",
label: t("MakeInvitationLink"),
onClick: onInvitationDialogClick
}/* ,
} /* ,
{
key: "send-invitation",
label: t("SendInviteAgain"),
onClick: onSentInviteAgain
} */
];
}, [settings, t, goToEmployeeCreate, goToGuestCreate, goToGroupCreate, onInvitationDialogClick/* , onSentInviteAgain */]);
}, [
settings,
t,
goToEmployeeCreate,
goToGuestCreate,
goToGroupCreate,
onInvitationDialogClick /* , onSentInviteAgain */
]);
const isArticlePinned = window.localStorage.getItem('asc_article_pinned_key');
const isArticlePinned = window.localStorage.getItem("asc_article_pinned_key");
return (
<StyledContainer isHeaderVisible={isHeaderVisible} isArticlePinned={isArticlePinned}>
{deleteDialog &&
<StyledContainer
isHeaderVisible={isHeaderVisible}
isArticlePinned={isArticlePinned}
>
{deleteDialog && (
<DeleteGroupUsersDialog
visible={deleteDialog}
onClose={onDelete}
users={selection}
filter={filter}
settings={settings}
history={history}
/>
}
)}
{sendInviteDialog && (
<SendInviteDialog
visible={sendInviteDialog}
onClose={onSentInviteAgain}
users={selection}
/>
)}
{isHeaderVisible ? (
<div className="group-button-menu-container">
@ -308,51 +337,63 @@ const SectionHeaderContent = props => {
/>
</div>
) : (
<div className="header-container">
{group ? (
<>
<Headline className='headline-header' type="content" truncate={true}>{group.name}</Headline>
{isAdmin && (
<div className="header-container">
{group ? (
<>
<Headline
className="headline-header"
type="content"
truncate={true}
>
{group.name}
</Headline>
{isAdmin && (
<ContextMenuButton
className="action-button"
directionX="right"
title={t("Actions")}
iconName="VerticalDotsIcon"
size={16}
color="#A3A9AE"
getData={getContextOptionsGroup}
isDisabled={false}
/>
)}
</>
) : (
<>
<Headline
className="headline-header"
truncate={true}
type="content"
>
{settings.customNames.groupsCaption}
</Headline>
{isAdmin && (
<>
<ContextMenuButton
className="action-button"
directionX="right"
title={t("Actions")}
iconName="VerticalDotsIcon"
iconName="PlusIcon"
size={16}
color="#A3A9AE"
getData={getContextOptionsGroup}
color="#657077"
getData={getContextOptionsPlus}
isDisabled={false}
/>
)}
</>
) : (
<>
<Headline className='headline-header' truncate={true} type="content">{settings.customNames.groupsCaption}</Headline>
{isAdmin && (
<>
<ContextMenuButton
className="action-button"
directionX="right"
title={t("Actions")}
iconName="PlusIcon"
size={16}
color="#657077"
getData={getContextOptionsPlus}
isDisabled={false}
/>
{dialogVisible &&
<InviteDialog
visible={dialogVisible}
onClose={onInvitationDialogClick}
onCloseButton={onInvitationDialogClick}
/>
}
</>
{dialogVisible && (
<InviteDialog
visible={dialogVisible}
onClose={onInvitationDialogClick}
onCloseButton={onInvitationDialogClick}
/>
)}
</>
)}
</div>
)}
</>
)}
</div>
)}
</StyledContainer>
);
};
@ -379,7 +420,10 @@ const mapStateToProps = state => {
};
};
export default connect(
mapStateToProps,
{ updateUserStatus, updateUserType, fetchPeople, deleteGroup, removeUser }
)(withTranslation()(withRouter(SectionHeaderContent)));
export default connect(mapStateToProps, {
updateUserStatus,
updateUserType,
fetchPeople,
deleteGroup,
removeUser
})(withTranslation()(withRouter(SectionHeaderContent)));

View File

@ -78,6 +78,13 @@
"UserControlsCommonResource":[
"NotBeUndone"
]
},
"SendInviteDialog":{
"Resource": [
"OKButton",
"CancelButton",
"SendInviteAgain"
]
}
},
"pages": {

View File

@ -195,7 +195,7 @@ export function getUserType(users) {
}
export function getInactiveUsers(users) {
const disabledStatus = users.filter(x => x.activationStatus === 2);
const disabledStatus = users.filter(x => x.activationStatus === 2 && x.status === 1);
return !disabledStatus.length;
}