Client: Added a notification about how much the quota is exceeded when trying to invite a paid user.
This commit is contained in:
parent
6e5dd2eccf
commit
3094409fa8
@ -32,7 +32,7 @@ import React, {
|
||||
useRef,
|
||||
} from "react";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withTranslation, Trans } from "react-i18next";
|
||||
|
||||
import { DeviceType, EmployeeType } from "@docspace/shared/enums";
|
||||
import { LOADER_TIMEOUT } from "@docspace/shared/constants";
|
||||
@ -60,6 +60,10 @@ import { Scrollbar } from "@docspace/shared/components/scrollbar";
|
||||
|
||||
import InfoBar from "./sub-components/InfoBar";
|
||||
import InvitePanelLoader from "./sub-components/InvitePanelLoader";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
import { ColorTheme, ThemeId } from "@docspace/shared/components/color-theme";
|
||||
|
||||
const InvitePanel = ({
|
||||
folders,
|
||||
@ -81,6 +85,9 @@ const InvitePanel = ({
|
||||
getUsersList,
|
||||
filter,
|
||||
currentDeviceType,
|
||||
isRoomAdmin,
|
||||
maxCountManagersByQuota,
|
||||
invitePaidUsersCount,
|
||||
}) => {
|
||||
const [invitePanelIsLoding, setInvitePanelIsLoading] = useState(
|
||||
roomId !== -1,
|
||||
@ -245,6 +252,22 @@ const InvitePanel = ({
|
||||
return () => document.removeEventListener("keyup", onKeyPress);
|
||||
});
|
||||
|
||||
const onClickPayments = () => {
|
||||
const paymentPageUrl = combineUrl(
|
||||
"/portal-settings",
|
||||
"/payments/portal-payments",
|
||||
);
|
||||
|
||||
toastr.clear();
|
||||
|
||||
window.DocSpace.navigate(paymentPageUrl);
|
||||
|
||||
setInvitePanelOptions({
|
||||
visible: false,
|
||||
hideSelector: false,
|
||||
defaultAccess: 1,
|
||||
});
|
||||
};
|
||||
const onClickSend = async (e) => {
|
||||
const invitations = inviteItems.map((item) => {
|
||||
let newItem = {};
|
||||
@ -289,7 +312,40 @@ const InvitePanel = ({
|
||||
updateInfoPanelMembers(t);
|
||||
}
|
||||
} catch (err) {
|
||||
toastr.error(err);
|
||||
let error = err;
|
||||
|
||||
if (err?.response?.status === 402) {
|
||||
error = (
|
||||
<>
|
||||
<Text as="span">
|
||||
{t("Common:PaidUsersExceedsLimit", {
|
||||
count: maxCountManagersByQuota + invitePaidUsersCount,
|
||||
limit: maxCountManagersByQuota,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
{!isRoomAdmin && (
|
||||
<Trans
|
||||
t={t}
|
||||
i18nKey="ChangeUserPermissions"
|
||||
ns="Common"
|
||||
components={{
|
||||
1: (
|
||||
<ColorTheme
|
||||
tag="a"
|
||||
themeId={ThemeId.Link}
|
||||
onClick={onClickPayments}
|
||||
target="_blank"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
toastr.error(error);
|
||||
setIsLoading(false);
|
||||
} finally {
|
||||
if (roomId === -1) {
|
||||
@ -462,6 +518,8 @@ export default inject(
|
||||
filesStore,
|
||||
dialogsStore,
|
||||
infoPanelStore,
|
||||
authStore,
|
||||
currentQuotaStore,
|
||||
}) => {
|
||||
const { theme, currentDeviceType } = settingsStore;
|
||||
|
||||
@ -480,11 +538,16 @@ export default inject(
|
||||
setInviteItems,
|
||||
setInvitePanelOptions,
|
||||
setInviteLanguage,
|
||||
invitePaidUsersCount,
|
||||
} = dialogsStore;
|
||||
|
||||
const { getFolderInfo, setRoomSecurity, getRoomSecurityInfo, folders } =
|
||||
filesStore;
|
||||
|
||||
const { isRoomAdmin } = authStore;
|
||||
|
||||
const { maxCountManagersByQuota } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
folders,
|
||||
setInviteLanguage,
|
||||
@ -506,6 +569,9 @@ export default inject(
|
||||
getUsersList,
|
||||
filter,
|
||||
currentDeviceType,
|
||||
isRoomAdmin,
|
||||
maxCountManagersByQuota,
|
||||
invitePaidUsersCount,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -93,7 +93,8 @@ const InviteInput = ({
|
||||
i18n,
|
||||
setCultureKey,
|
||||
standalone,
|
||||
isPaidUserLimit,
|
||||
isPaidUserAccess,
|
||||
setInvitePaidUsersCount,
|
||||
}) => {
|
||||
const isPublicRoomType = roomType === RoomsType.PublicRoom;
|
||||
|
||||
@ -136,6 +137,8 @@ const InviteInput = ({
|
||||
|
||||
if (addresses.length > 1) {
|
||||
return addresses.map((address) => {
|
||||
isPaidUserAccess(selectedAccess) && setInvitePaidUsersCount();
|
||||
|
||||
return {
|
||||
email: address.email,
|
||||
id: uid(),
|
||||
@ -147,6 +150,8 @@ const InviteInput = ({
|
||||
});
|
||||
}
|
||||
|
||||
isPaidUserAccess(selectedAccess) && setInvitePaidUsersCount();
|
||||
|
||||
return {
|
||||
email: addresses[0].email,
|
||||
id: uid(),
|
||||
@ -591,10 +596,11 @@ export default inject(
|
||||
inviteItems,
|
||||
setInviteLanguage,
|
||||
culture,
|
||||
setInvitePaidUsersCount,
|
||||
isPaidUserAccess,
|
||||
} = dialogsStore;
|
||||
|
||||
const { culture: language, standalone } = settingsStore;
|
||||
const { isPaidUserLimit } = currentQuotaStore;
|
||||
|
||||
return {
|
||||
language,
|
||||
@ -607,7 +613,8 @@ export default inject(
|
||||
defaultAccess: invitePanelOptions.defaultAccess,
|
||||
isOwner,
|
||||
standalone,
|
||||
isPaidUserLimit,
|
||||
isPaidUserAccess,
|
||||
setInvitePaidUsersCount,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -27,14 +27,16 @@
|
||||
import InfoEditReactSvgUrl from "PUBLIC_DIR/images/info.edit.react.svg?url";
|
||||
import AtReactSvgUrl from "PUBLIC_DIR/images/@.react.svg?url";
|
||||
import AlertSvgUrl from "PUBLIC_DIR/images/icons/12/alert.react.svg?url";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Avatar } from "@docspace/shared/components/avatar";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import { parseAddresses } from "@docspace/shared/utils";
|
||||
import { getAccessOptions } from "../utils";
|
||||
import { getUserTypeLabel } from "@docspace/shared/utils/common";
|
||||
|
||||
import { getAccessOptions } from "../utils";
|
||||
import {
|
||||
StyledEditInput,
|
||||
StyledEditButton,
|
||||
@ -61,6 +63,8 @@ const Item = ({
|
||||
setIsOpenItemAccess,
|
||||
isMobileView,
|
||||
standalone,
|
||||
isPaidUserAccess,
|
||||
setInvitePaidUsersCount,
|
||||
}) => {
|
||||
const {
|
||||
avatar,
|
||||
@ -162,7 +166,9 @@ const Item = ({
|
||||
const errors = !!parseErrors.length ? parseErrors : [];
|
||||
|
||||
setParseErrors(errors);
|
||||
changeInviteItem({ id, email: value, errors }).then(() => errorsInList());
|
||||
changeInviteItem({ id, email: value, errors, access }).then(() =>
|
||||
errorsInList(),
|
||||
);
|
||||
};
|
||||
|
||||
const changeValue = (e) => {
|
||||
@ -176,6 +182,8 @@ const Item = ({
|
||||
const removeItem = () => {
|
||||
const newItems = inviteItems.filter((item) => item.id !== id);
|
||||
|
||||
if (isPaidUserAccess(item.access)) setInvitePaidUsersCount(-1);
|
||||
|
||||
setInviteItems(newItems);
|
||||
};
|
||||
|
||||
@ -279,4 +287,11 @@ const Item = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default Item;
|
||||
export default inject(({ dialogsStore }) => {
|
||||
const { isPaidUserAccess, setInvitePaidUsersCount } = dialogsStore;
|
||||
|
||||
return {
|
||||
isPaidUserAccess,
|
||||
setInvitePaidUsersCount,
|
||||
};
|
||||
})(observer(Item));
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
import { getNewFiles } from "@docspace/shared/api/files";
|
||||
import {
|
||||
EmployeeType,
|
||||
FilesSelectorFilterTypes,
|
||||
FilterType,
|
||||
ShareAccessRights,
|
||||
@ -128,6 +129,8 @@ class DialogsStore {
|
||||
file: null,
|
||||
};
|
||||
|
||||
invitePaidUsersCount = 0;
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
treeFoldersStore,
|
||||
@ -433,10 +436,34 @@ class DialogsStore {
|
||||
this.inviteItems = inviteItems;
|
||||
};
|
||||
|
||||
setInvitePaidUsersCount = (modifier = 1) => {
|
||||
this.invitePaidUsersCount = this.invitePaidUsersCount + modifier;
|
||||
};
|
||||
|
||||
isPaidUserAccess = (selectedAccess) => {
|
||||
return (
|
||||
selectedAccess === EmployeeType.Admin ||
|
||||
selectedAccess === EmployeeType.Collaborator ||
|
||||
selectedAccess === EmployeeType.User
|
||||
);
|
||||
};
|
||||
|
||||
changeInviteItem = async (item) =>
|
||||
runInAction(() => {
|
||||
const index = this.inviteItems.findIndex((iItem) => iItem.id === item.id);
|
||||
|
||||
const isPrevAccessPaid = this.isPaidUserAccess(
|
||||
this.inviteItems[index].access,
|
||||
);
|
||||
const isCurrAccessPaid = this.isPaidUserAccess(item.access);
|
||||
|
||||
let modifier = 0;
|
||||
|
||||
if (isPrevAccessPaid && !isCurrAccessPaid) modifier = -1;
|
||||
if (!isPrevAccessPaid && isCurrAccessPaid) modifier = 1;
|
||||
|
||||
this.setInvitePaidUsersCount(modifier);
|
||||
|
||||
this.inviteItems[index] = { ...this.inviteItems[index], ...item };
|
||||
});
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
"Bytes": "bytes",
|
||||
"CancelButton": "Cancel",
|
||||
"ChangeButton": "Change",
|
||||
"ChangeUserPermissions": "Change user permissions to the invited people or <1>upgrade your tariff plan</1>.",
|
||||
"ChangeQuota": "Change quota",
|
||||
"ChangesSavedSuccessfully": "Changes saved successfully",
|
||||
"ChooseFromTemplates": "Choose from Templates",
|
||||
@ -320,6 +321,7 @@
|
||||
"Owner": "Owner",
|
||||
"PageOfTotalPage": "{{page}} of {{totalPage}}",
|
||||
"Paid": "Paid",
|
||||
"PaidUsersExceedsLimit": "The number of admins/power users exceeds the limit: {{count}} / {{limit}}.",
|
||||
"Password": "Password",
|
||||
"PasswordLimitDigits": "digits",
|
||||
"PasswordLimitMessage": "Password must contain",
|
||||
|
Loading…
Reference in New Issue
Block a user