Merge pull request #918 from ONLYOFFICE/feature/bar-quotas

Feature/bar quotas
This commit is contained in:
Alexey Safronov 2022-10-17 19:00:49 +03:00 committed by GitHub
commit 27713eed69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 584 additions and 69 deletions

View File

@ -0,0 +1,10 @@
{
"ClickHere": "click here",
"ConfirmEmailHeader": "Please activate your email to get access to all the portal features.",
"ConfirmEmailDescription": "Use the link provided in the activation email. Haven't received an email with the activation link?",
"RequestActivation": "Request activation once again",
"RoomQuotaHeader": "Rooms is about to be exceeded: {{currentValue}} / {{maxValue}}",
"RoomQuotaDescription": "You can archived the unnecessary rooms or <1>{{clickHere}}</1> to find a better pricing plan for your portal.",
"StorageQuotaHeader": "Storage space amount is about to be exceeded: {{currentValue}} / {{maxValue}}",
"StorageQuotaDescription": "You can remove the unnecessary files or <1>{{clickHere}}</1> to find a better pricing plan for your portal."
}

View File

@ -8,6 +8,7 @@
"EditPhoto": "Edit photo",
"EditSubscriptionsBtn": "Edit subscriptions",
"EditUser": "Edit profile",
"EmailNotVerified": "Email not verified",
"InterfaceTheme": "Interface theme",
"InviteAgainLbl": "Invite again",
"LightTheme": "Light theme",
@ -17,6 +18,7 @@
"PhoneLbl": "Phone",
"ProviderSuccessfullyConnected": "Provider successfully connected",
"ProviderSuccessfullyDisconnected": "Provider successfully disconnected",
"SendAgain": "Send again",
"ShowBackupCodes": "Show backup codes",
"SubscriptionEmailTipsToggleLbl": "Email notification with tips and tricks",
"SubscriptionTurnOffToast": "You have been successfully unsubscribed from the the mailing list. <1>Subscribe again</1>",

View File

@ -40,6 +40,7 @@ const MobileView = ({
primaryProgressDataPercent,
primaryProgressDataLoadingFile,
primaryProgressDataAlert,
primaryProgressDataErrors,
clearPrimaryProgressData,
secondaryProgressDataStoreVisible,
secondaryProgressDataStorePercent,
@ -98,7 +99,7 @@ const MobileView = ({
icon: "/static/images/cross.sidebar.react.svg",
percent: primaryProgressDataPercent,
status:
primaryProgressDataPercent === 100
primaryProgressDataPercent === 100 && !primaryProgressDataErrors
? t("FilesUploaded")
: `${currentPrimaryNumEl}/${files.length}`,
onClick: showUploadPanel,
@ -142,6 +143,7 @@ const MobileView = ({
primaryProgressDataVisible,
primaryProgressDataPercent,
primaryProgressDataLoadingFile,
primaryProgressDataErrors,
secondaryProgressDataStoreVisible,
secondaryProgressDataStorePercent,
secondaryProgressDataStoreCurrentFile,
@ -180,6 +182,7 @@ export default inject(({ uploadDataStore }) => {
percent: primaryProgressDataPercent,
loadingFile: primaryProgressDataLoadingFile,
alert: primaryProgressDataAlert,
errors: primaryProgressDataErrors,
clearPrimaryProgressData,
} = primaryProgressDataStore;
@ -199,6 +202,7 @@ export default inject(({ uploadDataStore }) => {
primaryProgressDataPercent,
primaryProgressDataLoadingFile,
primaryProgressDataAlert,
primaryProgressDataErrors,
clearPrimaryProgressData,
secondaryProgressDataStoreVisible,
secondaryProgressDataStorePercent,

View File

@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import { CreateRoomDialog } from "../dialogs";
import { toastr } from "@docspace/components";
const CreateRoomEvent = ({
visible,
@ -82,6 +83,7 @@ const CreateRoomEvent = ({
img.src = url;
});
} catch (err) {
toastr.error(err);
console.log(err);
} finally {
await updateCurrentFolder(null, currrentFolderId);

View File

@ -1,27 +1,66 @@
import React, { useEffect, useState } from "react";
import { inject, observer } from "mobx-react";
import difference from "lodash/difference";
import { withTranslation } from "react-i18next";
import { withRouter } from "react-router";
import { ADS_TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
import { getBannerAttribute } from "@docspace/components/utils/banner";
import { getConvertedSize } from "@docspace/common/utils";
import { getBannerAttribute } from "@docspace/components/utils/banner";
import SnackBar from "@docspace/components/snackbar";
const bannerHOC = (props) => {
const { firstLoad, setMaintenanceExist } = props;
import QuotasBar from "./QuotasBar";
import ConfirmEmailBar from "./ConfirmEmailBar";
const CONFIRM_EMAIL = "confirm-email";
const ROOM_QUOTA = "room-quota";
const STORAGE_QUOTA = "storage-quota";
const Bar = (props) => {
const {
t,
tReady,
firstLoad,
isAdmin,
setMaintenanceExist,
withActivationBar,
sendActivationLink,
onPaymentsClick,
maxCountRoomsByQuota,
usedRoomsCount,
maxTotalSizeByQuota,
usedTotalStorageSizeCount,
showRoomQuotaBar,
showStorageQuotaBar,
} = props;
const [barVisible, setBarVisible] = useState({
roomQuota: false,
storageQuota: false,
confirmEmail: false,
});
const [htmlLink, setHtmlLink] = useState();
const [campaigns, setCampaigns] = useState();
const { loadLanguagePath } = getBannerAttribute();
const bar = (localStorage.getItem("bar") || "")
.split(",")
.filter((bar) => bar.length > 0);
const updateBanner = async () => {
const bar = (localStorage.getItem("bar") || "")
.split(",")
.filter((bar) => bar.length > 0);
const closed = JSON.parse(localStorage.getItem("barClose"));
const banner = difference(bar, closed);
let index = Number(localStorage.getItem("barIndex") || 0);
if (banner.length < 1 || index + 1 >= banner.length) {
@ -30,8 +69,29 @@ const bannerHOC = (props) => {
index++;
}
if (closed) {
if (!closed.includes(ROOM_QUOTA) && isAdmin) {
setBarVisible((value) => ({ ...value, roomQuota: true }));
}
if (!closed.includes(STORAGE_QUOTA) && isAdmin) {
setBarVisible((value) => ({ ...value, storageQuota: true }));
}
if (!closed.includes(CONFIRM_EMAIL)) {
setBarVisible((value) => ({ ...value, confirmEmail: true }));
}
} else {
setBarVisible({
roomQuota: isAdmin,
storageQuota: isAdmin,
confirmEmail: true,
});
}
try {
const [htmlUrl, campaigns] = await loadLanguagePath();
setHtmlLink(htmlUrl);
setCampaigns(campaigns);
} catch (e) {
@ -51,6 +111,51 @@ const bannerHOC = (props) => {
};
}, []);
const sendActivationLinkAction = () => {
if (sendActivationLink) {
sendActivationLink(t).finally(() => {
return onCloseActivationBar();
});
} else {
onCloseActivationBar();
}
};
const onCloseActivationBar = () => {
const closeItems = JSON.parse(localStorage.getItem("barClose")) || [];
const closed =
closeItems.length > 0 ? [...closeItems, CONFIRM_EMAIL] : [CONFIRM_EMAIL];
localStorage.setItem("barClose", JSON.stringify(closed));
setBarVisible((value) => ({ ...value, confirmEmail: false }));
setMaintenanceExist(false);
};
const onClickQuota = (isRoomQuota) => {
onPaymentsClick && onPaymentsClick();
onCloseQuota(isRoomQuota);
};
const onCloseQuota = (isRoomQuota) => {
const currentBar = isRoomQuota ? ROOM_QUOTA : STORAGE_QUOTA;
const closeItems = JSON.parse(localStorage.getItem("barClose")) || [];
const closed =
closeItems.length > 0 ? [...closeItems, currentBar] : [currentBar];
localStorage.setItem("barClose", JSON.stringify(closed));
setBarVisible((value) =>
isRoomQuota
? { ...value, roomQuota: false }
: { ...value, storageQuota: false }
);
setMaintenanceExist(false);
};
const onClose = () => {
setMaintenanceExist(false);
const closeItems = JSON.parse(localStorage.getItem("barClose")) || [];
@ -64,7 +169,33 @@ const bannerHOC = (props) => {
setMaintenanceExist(true);
};
return htmlLink && !firstLoad ? (
const isRoomQuota = showRoomQuotaBar && barVisible.roomQuota;
const isStorageQuota = showStorageQuotaBar && barVisible.storageQuota;
const quotasValue = {
maxValue: isRoomQuota
? maxCountRoomsByQuota
: getConvertedSize(t, maxTotalSizeByQuota),
currentValue: isRoomQuota
? usedRoomsCount
: getConvertedSize(t, usedTotalStorageSizeCount),
};
return (isRoomQuota || isStorageQuota) && tReady ? (
<QuotasBar
isRoomQuota={isRoomQuota}
{...quotasValue}
onClick={onClickQuota}
onClose={onCloseQuota}
onLoad={onLoad}
/>
) : withActivationBar && barVisible.confirmEmail && tReady ? (
<ConfirmEmailBar
onLoad={onLoad}
onClick={sendActivationLinkAction}
onClose={onCloseActivationBar}
/>
) : htmlLink && !firstLoad && tReady ? (
<SnackBar
onLoad={onLoad}
clickAction={onClose}
@ -74,4 +205,36 @@ const bannerHOC = (props) => {
) : null;
};
export default bannerHOC;
export default inject(({ auth, profileActionsStore }) => {
const { user, withActivationBar, sendActivationLink } = auth.userStore;
const { onPaymentsClick } = profileActionsStore;
const {
maxCountRoomsByQuota,
usedRoomsCount,
maxTotalSizeByQuota,
usedTotalStorageSizeCount,
showRoomQuotaBar,
showStorageQuotaBar,
} = auth.currentQuotaStore;
return {
isAdmin: user?.isAdmin,
withActivationBar,
sendActivationLink,
onPaymentsClick,
maxCountRoomsByQuota,
usedRoomsCount,
maxTotalSizeByQuota,
usedTotalStorageSizeCount,
showRoomQuotaBar,
showStorageQuotaBar,
};
})(withTranslation(["Profile", "Common"])(withRouter(observer(Bar))));

View File

@ -0,0 +1,35 @@
import React from "react";
import { withTranslation } from "react-i18next";
import styled from "styled-components";
import SnackBar from "@docspace/components/snackbar";
import Link from "@docspace/components/link";
const StyledLink = styled(Link)`
font-size: 12px;
line-height: 16px;
font-weight: 400;
color: #316daa;
`;
const ConfirmEmailBar = ({ t, onClick, onClose, onLoad }) => {
return (
<SnackBar
headerText={t("ConfirmEmailHeader")}
text={
<>
{t("ConfirmEmailDescription")}{" "}
<StyledLink onClick={onClick}>{t("RequestActivation")}</StyledLink>
</>
}
isCampaigns={false}
opacity={1}
onLoad={onLoad}
clickAction={onClose}
/>
);
};
export default withTranslation(["MainBar"])(ConfirmEmailBar);

View File

@ -0,0 +1,81 @@
import React from "react";
import { withTranslation, Trans } from "react-i18next";
import styled, { css } from "styled-components";
import SnackBar from "@docspace/components/snackbar";
import Link from "@docspace/components/link";
const StyledLink = styled(Link)`
font-size: 12px;
line-height: 16px;
font-weight: 400;
color: #316daa;
`;
const QuotasBar = ({
t,
isRoomQuota,
currentValue,
maxValue,
onClick,
onClose,
onLoad,
}) => {
const onClickAction = () => {
onClick && onClick(isRoomQuota);
};
const onCloseAction = () => {
onClose && onClose(isRoomQuota);
};
const roomQuota = {
header: t("RoomQuotaHeader", { currentValue, maxValue }),
description: (
<Trans i18nKey="RoomQuotaDescription" t={t}>
You can archived the unnecessary rooms or
<StyledLink onClick={onClickAction}>
{{ clickHere: t("ClickHere") }}
</StyledLink>{" "}
to find a better pricing plan for your portal.
</Trans>
),
};
const storageQuota = {
header: t("StorageQuotaHeader", { currentValue, maxValue }),
description: (
<Trans i18nKey="StorageQuotaQuotaDescription" t={t}>
You can remove the unnecessary files or{" "}
<StyledLink onClick={onClickAction}>
{{ clickHere: t("ClickHere") }}
</StyledLink>{" "}
to find a better pricing plan for your portal.
</Trans>
),
};
return isRoomQuota ? (
<SnackBar
headerText={roomQuota.header}
text={roomQuota.description}
isCampaigns={false}
opacity={1}
onLoad={onLoad}
clickAction={onCloseAction}
/>
) : (
<SnackBar
headerText={storageQuota.header}
text={storageQuota.description}
isCampaigns={false}
opacity={1}
onLoad={onLoad}
clickAction={onCloseAction}
/>
);
};
export default withTranslation(["MainBar"])(QuotasBar);

View File

@ -5,8 +5,6 @@ import { isMobileOnly } from "react-device-detect";
import Bar from "./Bar";
import SnackBar from "@docspace/components/snackbar";
const StyledContainer = styled.div`
width: 100%;
max-width: 100%;
@ -30,18 +28,13 @@ const StyledContainer = styled.div`
}
`;
//TODO: remove commented code after update bar logic
const MainBar = ({
firstLoad,
checkedMaintenance,
snackbarExist,
setMaintenanceExist,
}) => {
// const [isVisible, setIsVisible] = React.useState(false);
React.useEffect(() => {
// setTimeout(() => setIsVisible(true), 9000);
return () => setMaintenanceExist && setMaintenanceExist(false);
}, []);
@ -50,17 +43,6 @@ const MainBar = ({
{checkedMaintenance && !snackbarExist && (
<Bar firstLoad={firstLoad} setMaintenanceExist={setMaintenanceExist} />
)}
{/* {isVisible && (
<SnackBar
headerText={"Rooms is about to be exceeded: 10 / 12"}
text={
"You can archived the unnecessary rooms or click here to find a better pricing plan for your portal."
}
isCampaigns={false}
opacity={1}
onLoad={() => console.log("load snackbar")}
/>
)} */}
</StyledContainer>
);
};

View File

@ -1,4 +1,5 @@
import React from "react";
import { ReactSVG } from "react-svg";
import { useTranslation } from "react-i18next";
import { inject, observer } from "mobx-react";
@ -19,6 +20,7 @@ import {
} from "SRC_DIR/components/dialogs";
import { StyledWrapper, StyledInfo } from "./styled-main-profile";
import { HelpButton } from "@docspace/components";
const MainProfile = (props) => {
const { t } = useTranslation(["Profile", "Common"]);
@ -33,13 +35,20 @@ const MainProfile = (props) => {
setChangeNameVisible,
changeAvatarVisible,
setChangeAvatarVisible,
withActivationBar,
sendActivationLink,
} = props;
const role = getUserRole(profile);
const sendActivationLinkAction = () => {
sendActivationLink && sendActivationLink(t);
};
return (
<StyledWrapper>
<Avatar
className={"avatar"}
size="max"
role={role}
source={profile.avatarMax}
@ -68,7 +77,33 @@ const MainProfile = (props) => {
<Text as="div" color="#A3A9AE" className="label">
{t("Common:Email")}
</Text>
<Text fontWeight={600}>{profile.email}</Text>
<Text
as="div"
className={"email-text-container"}
fontWeight={600}
>
{profile.email}
{withActivationBar && (
<HelpButton
className="send-again-icon"
color={"#316daa"}
tooltipContent={t("EmailNotVerified")}
iconName={"images/send.clock.react.svg"}
/>
)}
</Text>
{withActivationBar && (
<Text
className="send-again-text"
fontWeight={600}
noSelect
truncate
onClick={sendActivationLinkAction}
>
{t("SendAgain")}
</Text>
)}
</div>
<IconButton
className="edit-button"
@ -76,6 +111,25 @@ const MainProfile = (props) => {
size="12"
onClick={() => setChangeEmailVisible(true)}
/>
{withActivationBar && (
<div className="send-again-container">
<HelpButton
className="send-again-icon"
color={"#316daa"}
tooltipContent={t("EmailNotVerified")}
iconName={"images/send.clock.react.svg"}
/>
<Text
className="send-again-text"
fontWeight={600}
noSelect
truncate
onClick={sendActivationLinkAction}
>
{t("SendAgain")}
</Text>
</div>
)}
</div>
<div className="row">
<div className="field">
@ -131,9 +185,11 @@ const MainProfile = (props) => {
);
};
export default inject(({ peopleStore }) => {
export default inject(({ auth, peopleStore }) => {
const { targetUserStore } = peopleStore;
const { withActivationBar, sendActivationLink } = auth.userStore;
const {
targetUser: profile,
changeEmailVisible,
@ -156,5 +212,7 @@ export default inject(({ peopleStore }) => {
setChangeNameVisible,
changeAvatarVisible,
setChangeAvatarVisible,
withActivationBar,
sendActivationLink,
};
})(observer(MainProfile));

View File

@ -6,12 +6,21 @@ import {
} from "@docspace/components/utils/device";
export const StyledWrapper = styled.div`
width: 100%;
max-width: 100%;
display: flex;
padding: 24px 24px 18px 24px;
gap: 40px;
background: ${(props) => props.theme.profile.main.background};
border-radius: 12px;
box-sizing: border-box;
.avatar {
min-width: 124px;
}
@media ${smallTablet} {
background: none;
flex-direction: column;
@ -22,6 +31,15 @@ export const StyledWrapper = styled.div`
`;
export const StyledInfo = styled.div`
width: 100%;
max-width: 100%;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
flex-direction: column;
gap: 12px;
@ -59,12 +77,77 @@ export const StyledInfo = styled.div`
}
}
.email-text-container {
padding-left: 8px;
}
.send-again-text {
line-height: 15px;
color: #316daa;
border-bottom: 1px solid #316daa;
width: fit-content;
display: none;
@media ${smallTablet} {
display: block;
cursor: pointer;
}
}
.send-again-icon {
display: none;
@media ${smallTablet} {
display: block;
padding-left: 5px;
max-width: 12px;
max-height: 12px;
}
}
.send-again-container {
display: flex;
align-items: center;
margin-left: 8px;
cursor: pointer;
.send-again-text {
display: block;
margin-left: 4px;
}
.send-again-icon {
display: block;
}
@media ${smallTablet} {
display: none;
.send-again-text,
.send-again-icon {
display: none;
}
}
}
.label {
min-width: 75px;
max-width: 75px;
white-space: nowrap;
}
.edit-button {
min-width: 12px;
}
@media ${smallTablet} {
gap: 8px;
background: ${(props) => props.theme.profile.main.background};
@ -75,6 +158,22 @@ export const StyledInfo = styled.div`
flex-direction: column;
gap: 2px;
.email-text-container {
display: flex;
.send-again-icon {
margin-left: 4px;
display: flex;
align-items: center;
div {
display: flex;
align-items: center;
}
}
}
& > p {
padding-left: 0;
font-size: 12px !important;
@ -91,6 +190,11 @@ export const StyledInfo = styled.div`
.edit-button {
margin-left: auto;
min-width: 12px;
}
.email-text-container {
padding-left: 0px;
}
}
}

View File

@ -7,6 +7,7 @@ class PrimaryProgressDataStore {
icon = "upload";
alert = false;
loadingFile = null;
errors = 0;
constructor() {
makeObservable(this, {
@ -16,9 +17,11 @@ class PrimaryProgressDataStore {
icon: observable,
alert: observable,
loadingFile: observable,
errors: observable,
setPrimaryProgressBarData: action,
clearPrimaryProgressData: action,
setPrimaryProgressBarErrors: action,
});
}
@ -36,12 +39,17 @@ class PrimaryProgressDataStore {
label: "",
icon: "",
alert: false,
errors: 0,
});
};
setPrimaryProgressBarShowError = (error) => {
this.alert = error;
};
setPrimaryProgressBarErrors = (errors) => {
this.errors = errors;
};
}
export default PrimaryProgressDataStore;

View File

@ -41,6 +41,7 @@ class UploadDataStore {
converted = true;
uploadPanelVisible = false;
selectedUploadFile = [];
errors = 0;
isUploading = false;
isUploadingAndConversion = false;
@ -105,6 +106,7 @@ class UploadDataStore {
this.conversionPercent = 0;
this.uploaded = true;
this.converted = true;
this.errors = 0;
this.isUploadingAndConversion = false;
this.isUploading = false;
@ -913,10 +915,16 @@ class UploadDataStore {
const { fetchFiles, filter } = this.filesStore;
const { withPaging } = this.authStore.settingsStore;
const totalErrorsCount = sumBy(this.files, (f) => (f.error ? 1 : 0));
const totalErrorsCount = sumBy(this.files, (f) => {
f.error && toastr.error(f.error);
return f.error ? 1 : 0;
});
if (totalErrorsCount > 0) {
this.primaryProgressDataStore.setPrimaryProgressBarShowError(true); // for empty file
this.primaryProgressDataStore.setPrimaryProgressBarErrors(
totalErrorsCount
);
console.log("Errors: ", totalErrorsCount);
}

View File

@ -45,7 +45,7 @@ class QuotasStore {
(obj) => obj.id === MANAGER
);
return result.value;
return result?.value;
}
get addedManagersCount() {
@ -53,7 +53,7 @@ class QuotasStore {
(obj) => obj.id === MANAGER
);
return result.used.value;
return result?.used?.value;
}
get maxTotalSizeByQuota() {
@ -61,16 +61,16 @@ class QuotasStore {
(obj) => obj.id === TOTAL_SIZE
);
if (!result.value) return PortalFeaturesLimitations.Limitless;
if (!result?.value) return PortalFeaturesLimitations.Limitless;
return result.value;
return result?.value;
}
get usedTotalStorageSizeCount() {
const result = this.currentPortalQuotaFeatures.find(
(obj) => obj.id === TOTAL_SIZE
);
return result.used.value;
return result?.used?.value;
}
get maxFileSizeByQuota() {
@ -78,30 +78,31 @@ class QuotasStore {
(obj) => obj.id === FILE_SIZE
);
return result.value;
return result?.value;
}
get maxCountUsersByQuota() {
const result = this.currentPortalQuotaFeatures.find(
(obj) => obj.id === USERS
);
if (!result || !result.value) return PortalFeaturesLimitations.Limitless;
return result.value;
if (!result || !result?.value) return PortalFeaturesLimitations.Limitless;
return result?.value;
}
get maxCountRoomsByQuota() {
const result = this.currentPortalQuotaFeatures.find(
(obj) => obj.id === ROOM
);
if (!result || !result.value) return PortalFeaturesLimitations.Limitless;
return result.value;
if (!result || !result?.value) return PortalFeaturesLimitations.Limitless;
return result?.value;
}
get usedRoomsCount() {
const result = this.currentPortalQuotaFeatures.find(
(obj) => obj.id === ROOM
);
return result.used.value;
return result?.used?.value;
}
get isBrandingAndCustomizationAvailable() {
@ -109,7 +110,7 @@ class QuotasStore {
(obj) => obj.id === "whitelabel"
);
return result.value;
return result?.value;
}
get isSSOAvailable() {
@ -117,7 +118,7 @@ class QuotasStore {
(obj) => obj.id === "sso"
);
return result.value;
return result?.value;
}
get isRestoreAndAutoBackupAvailable() {
@ -125,7 +126,7 @@ class QuotasStore {
(obj) => obj.id === "restore"
);
return result.value;
return result?.value;
}
get isAuditAvailable() {
@ -133,7 +134,7 @@ class QuotasStore {
(obj) => obj.id === "audit"
);
return result.value;
return result?.value;
}
get currentTariffPlanTitle() {
@ -144,9 +145,9 @@ class QuotasStore {
const result = [];
this.currentPortalQuotaFeatures.forEach((elem) => {
elem.id === ROOM && result.splice(0, 0, elem);
elem.id === MANAGER && result.splice(1, 0, elem);
elem.id === TOTAL_SIZE && result.splice(2, 0, elem);
elem.id === ROOM && result?.splice(0, 0, elem);
elem.id === MANAGER && result?.splice(1, 0, elem);
elem.id === TOTAL_SIZE && result?.splice(2, 0, elem);
});
return result;
@ -157,9 +158,22 @@ class QuotasStore {
(obj) => obj.id === USERS_IN_ROOM
);
if (!result || !result.value) return PortalFeaturesLimitations.Limitless;
if (!result || !result?.value) return PortalFeaturesLimitations.Limitless;
return result.value;
return result?.value;
}
get showRoomQuotaBar() {
return (
(this.usedRoomsCount / this.maxCountRoomsByQuota) * 100 >= 90 ||
this.maxCountRoomsByQuota - this.usedRoomsCount === 1
);
}
get showStorageQuotaBar() {
return (
(this.usedTotalStorageSizeCount / this.maxTotalSizeByQuota) * 100 >= 90
);
}
setPortalQuotaValue = (res) => {

View File

@ -1,5 +1,9 @@
import React from "react";
import { toastr } from "@docspace/components";
import { makeAutoObservable } from "mobx";
import { Trans } from "react-i18next";
import api from "../api";
import { EmployeeActivationStatus } from "../constants";
class UserStore {
user = null;
@ -7,6 +11,8 @@ class UserStore {
isLoaded = false;
userIsUpdate = false;
withSendAgain = true;
constructor() {
makeAutoObservable(this);
}
@ -69,6 +75,40 @@ class UserStore {
this.userIsUpdate = isUpdate;
};
setWithSendAgain = (withSendAgain) => {
this.withSendAgain = withSendAgain;
};
sendActivationLink = (t) => {
const { email, id } = this.user;
return api.people
.resendUserInvites([id])
.then(() => {
toastr.success(
<Trans
i18nKey="MessageEmailActivationInstuctionsSentOnEmail"
ns="People"
t={t}
>
The email activation instructions have been sent to the
<strong>{{ email: email }}</strong> email address
</Trans>
);
})
.finally(() => {
this.setWithSendAgain(false);
});
};
get withActivationBar() {
return (
this.user &&
this.user.activationStatus === EmployeeActivationStatus.Pending &&
this.withSendAgain
);
}
get isAuthenticated() {
return !!this.user;
}

View File

@ -22,7 +22,7 @@ import { isIOS, isMobile } from "react-device-detect";
import Backdrop from "../backdrop";
import styled from "styled-components";
import ButtonAlertIcon from "../../../public/images/main-button.alert.react.svg";
import ButtonAlertIcon from "../../../public/images/button.alert.react.svg";
import commonIconsStyles from "../utils/common-icons-style";
import { isMobileOnly } from "react-device-detect";
@ -395,7 +395,7 @@ const MainButtonMobile = (props) => {
)}
</StyledDropDown>
<StyledAlertIcon>
{alert && !isOpen ? <StyledButtonAlertIcon size="small" /> : <></>}
{alert && !isOpen ? <StyledButtonAlertIcon size="medium" /> : <></>}
</StyledAlertIcon>
</div>
</>

View File

@ -187,18 +187,18 @@ class SnackBar extends React.Component {
}
SnackBar.propTypes = {
text: PropType.string,
headerText: PropType.string,
btnText: PropType.string,
backgroundImg: PropType.string,
backgroundColor: PropType.string,
textColor: PropType.string,
showIcon: PropType.bool,
clickAction: PropType.func,
fontSize: PropType.string,
fontWeight: PropType.string,
textAlign: PropType.string,
htmlContent: PropType.string,
text: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
headerText: PropTypes.string,
btnText: PropTypes.string,
backgroundImg: PropTypes.string,
backgroundColor: PropTypes.string,
textColor: PropTypes.string,
showIcon: PropTypes.bool,
clickAction: PropTypes.func,
fontSize: PropTypes.string,
fontWeight: PropTypes.string,
textAlign: PropTypes.string,
htmlContent: PropTypes.string,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
countDownTime: PropType.number,
sectionWidth: PropTypes.number,

View File

@ -40,7 +40,7 @@ const StyledSnackBar = styled(Box)`
.header-body {
width: 100%;
height: 16px;
height: fit-content;
display: flex;
flex-direction: row;
align-items: center;
@ -52,7 +52,7 @@ const StyledSnackBar = styled(Box)`
line-height: 16px;
font-weight: 600;
text-align: center;
text-align: justify;
margin: 0;
}
@ -69,6 +69,8 @@ const StyledSnackBar = styled(Box)`
font-size: 12px;
line-height: 16px;
font-weight: 400;
text-align: justify;
}
}
}
@ -79,11 +81,12 @@ const StyledSnackBar = styled(Box)`
border: none;
font-size: inherit;
color: "#333";
margin: -2px 4px 4px 24px;
margin: 0px 4px 4px 24px;
padding: 0;
min-width: min-content;
cursor: pointer;
margin-left: auto;
margin-left: 12px;
text-decoration: underline;
}
@ -95,6 +98,7 @@ const StyledSnackBar = styled(Box)`
color: "#000";
cursor: pointer;
line-height: 14px;
text-decoration: underline;
}
`;