diff --git a/packages/client/src/components/panels/InvitePanel/index.js b/packages/client/src/components/panels/InvitePanel/index.js
index 7633df58a1..da952601bc 100644
--- a/packages/client/src/components/panels/InvitePanel/index.js
+++ b/packages/client/src/components/panels/InvitePanel/index.js
@@ -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 = (
+ <>
+
+ {t("Common:PaidUsersExceedsLimit", {
+ count: maxCountManagersByQuota + invitePaidUsersCount,
+ limit: maxCountManagersByQuota,
+ })}
+
+
+ {!isRoomAdmin && (
+
+ ),
+ }}
+ />
+ )}
+ >
+ );
+ }
+
+ 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,
};
},
)(
diff --git a/packages/client/src/components/panels/InvitePanel/sub-components/InviteInput.js b/packages/client/src/components/panels/InvitePanel/sub-components/InviteInput.js
index b810807b06..8fa310419f 100644
--- a/packages/client/src/components/panels/InvitePanel/sub-components/InviteInput.js
+++ b/packages/client/src/components/panels/InvitePanel/sub-components/InviteInput.js
@@ -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,
};
},
)(
diff --git a/packages/client/src/components/panels/InvitePanel/sub-components/Item.js b/packages/client/src/components/panels/InvitePanel/sub-components/Item.js
index 1bccecb7b6..4a27bf2003 100644
--- a/packages/client/src/components/panels/InvitePanel/sub-components/Item.js
+++ b/packages/client/src/components/panels/InvitePanel/sub-components/Item.js
@@ -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));
diff --git a/packages/client/src/store/DialogsStore.js b/packages/client/src/store/DialogsStore.js
index a642cfe6a8..26526d1971 100644
--- a/packages/client/src/store/DialogsStore.js
+++ b/packages/client/src/store/DialogsStore.js
@@ -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 };
});
diff --git a/public/locales/en/Common.json b/public/locales/en/Common.json
index 61acc3af18..21442aee9c 100644
--- a/public/locales/en/Common.json
+++ b/public/locales/en/Common.json
@@ -38,6 +38,7 @@
"Bytes": "bytes",
"CancelButton": "Cancel",
"ChangeButton": "Change",
+ "ChangeUserPermissions": "Change user permissions to the invited people or <1>upgrade your tariff plan1>.",
"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",