Merge pull request #918 from ONLYOFFICE/feature/bar-quotas
Feature/bar quotas
This commit is contained in:
commit
27713eed69
10
packages/client/public/locales/en/MainBar.json
Normal file
10
packages/client/public/locales/en/MainBar.json
Normal 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."
|
||||
}
|
@ -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>",
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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))));
|
||||
|
35
packages/client/src/components/MainBar/ConfirmEmailBar.js
Normal file
35
packages/client/src/components/MainBar/ConfirmEmailBar.js
Normal 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);
|
81
packages/client/src/components/MainBar/QuotasBar.js
Normal file
81
packages/client/src/components/MainBar/QuotasBar.js
Normal 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);
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
`;
|
||||
|
Loading…
Reference in New Issue
Block a user