Merge branch 'develop' into feature/list-accounts-additional-info

This commit is contained in:
Dmitry Sychugov 2024-02-01 12:50:35 +05:00
commit e4339f721f
43 changed files with 565 additions and 447 deletions

View File

@ -0,0 +1,63 @@
const ORANGE = "#F97A0B";
const RED = "#F2665A";
export const getSaasBar = (
t,
isPaymentPageAvailable,
isNonProfit,
isFreeTariff,
isGracePeriod
) => {
if (
isPaymentPageAvailable &&
!isNonProfit &&
(isFreeTariff || isGracePeriod)
) {
if (isFreeTariff) return { label: t("Common:TryBusiness"), color: ORANGE };
if (isGracePeriod) return { label: t("Common:LatePayment"), color: RED };
}
};
export const getEnterpriseBar = (
t,
isPaymentPageAvailable,
isEnterprise,
isTrial,
isLicenseExpiring,
isLicenseDateExpired,
trialDaysLeft,
paymentDate
) => {
if (
isPaymentPageAvailable &&
isEnterprise &&
(isTrial || isLicenseExpiring || isLicenseDateExpired)
) {
if (isTrial) {
if (isLicenseDateExpired)
return { label: t("Common:TrialExpired"), color: ORANGE };
return {
label: t("Common:TrialDaysLeft", { count: trialDaysLeft }),
color: ORANGE,
};
} else {
if (isLicenseDateExpired)
return {
label: t("Common:SubscriptionExpiredTitle"),
color: RED,
};
return {
label: t("Common:SubscriptionIsExpiring", { date: paymentDate }),
color: ORANGE,
};
}
}
};
export const checkBar = () => {
const el = document.getElementById("tariff-bar-text");
el?.classList?.remove("hidden");
if (el?.offsetWidth < el?.scrollWidth) {
el?.classList?.add("hidden");
}
};

View File

@ -0,0 +1,135 @@
import { useEffect } from "react";
import styled from "styled-components";
import { useNavigate } from "react-router-dom";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import { combineUrl } from "@docspace/shared/utils/combineUrl";
import { Text } from "@docspace/shared/components/text";
import { getSaasBar, getEnterpriseBar, checkBar } from "./helpers";
const StyledWrapper = styled.div`
display: grid;
cursor: pointer;
#tariff-bar-text:hover {
opacity: 0.85;
}
#tariff-bar-text:active {
filter: brightness(0.9);
}
.hidden {
visibility: hidden;
}
`;
const PROXY_BASE_URL = combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings"
);
const TariffBar = ({
isEnterprise,
isNonProfit,
isGracePeriod,
isFreeTariff,
isPaymentPageAvailable,
isLicenseExpiring,
isLicenseDateExpired,
isTrial,
standalone,
paymentDate,
trialDaysLeft,
title,
}) => {
const navigate = useNavigate();
const { t } = useTranslation("Common");
const onClick = () => {
const paymentPageUrl = combineUrl(
PROXY_BASE_URL,
"/payments/portal-payments"
);
navigate(paymentPageUrl);
};
useEffect(() => {
checkBar();
}, []);
useEffect(() => {
checkBar();
}, [title]);
const tariffBar = !standalone
? getSaasBar(
t,
isPaymentPageAvailable,
isNonProfit,
isFreeTariff,
isGracePeriod
)
: getEnterpriseBar(
t,
isPaymentPageAvailable,
isEnterprise,
isTrial,
isLicenseExpiring,
isLicenseDateExpired,
trialDaysLeft,
paymentDate
);
if (!tariffBar) return <></>;
return (
<StyledWrapper>
<Text
id="tariff-bar-text"
as="div"
fontSize="12px"
fontWeight={600}
lineHeight="16px"
color={tariffBar.color}
onClick={onClick}
truncate={true}
>
{tariffBar.label}
</Text>
</StyledWrapper>
);
};
export default inject(({ auth }) => {
const {
settingsStore,
currentQuotaStore,
isPaymentPageAvailable,
currentTariffStatusStore,
isEnterprise,
} = auth;
const { isFreeTariff, isNonProfit, isTrial } = currentQuotaStore;
const {
isGracePeriod,
isLicenseExpiring,
isLicenseDateExpired,
paymentDate,
trialDaysLeft,
} = currentTariffStatusStore;
const { standalone } = settingsStore;
return {
isEnterprise,
isNonProfit,
isGracePeriod,
isFreeTariff,
isPaymentPageAvailable,
isLicenseExpiring,
isLicenseDateExpired,
isTrial,
standalone,
paymentDate,
trialDaysLeft,
};
})(observer(TariffBar));

View File

@ -22,6 +22,11 @@ class TagHandler {
addTag(name) {
let newTags = [...this.tags];
if (this.isAlreadyAdded(name)) {
return; //already added
}
newTags.push({
id: this.createRandomTagId(),
name,
@ -29,11 +34,26 @@ class TagHandler {
this.setTags(newTags);
}
isAlreadyAdded(name) {
return !!this.tags.find((t) => t.name.toLowerCase() === name.toLowerCase());
}
isNew(name) {
return !this.fetchedTags.find(
(t) => t.toLowerCase() === name.toLowerCase()
);
}
addNewTag(name) {
let newTags = [...this.tags];
if (this.isAlreadyAdded(name)) {
return; //already added
}
newTags.push({
id: this.createRandomTagId(),
isNew: true,
isNew: this.isNew(name),
name,
});
this.setTags(newTags);

View File

@ -119,7 +119,7 @@ const ReportDialog = (props) => {
onChange={onChangeTextareaValue}
autoFocus
areaSelect
heightTextArea={72}
heightTextArea="72px"
fontSize={13}
/>
<div className="report-wrapper">

View File

@ -145,7 +145,7 @@ const EmbeddingBody = ({ t, link, requestToken, roomId }) => {
iconName={CopyReactSvgUrl}
onClick={onCopyLink}
/>
<Textarea isReadOnly value={codeBlock} heightTextArea={150} />
<Textarea isReadOnly value={codeBlock} heightTextArea="150px" />
</div>
</div>
</StyledBody>

View File

@ -97,7 +97,7 @@ const CommentEditor = ({
onChange={onChangeInputValue}
autoFocus
areaSelect
heightTextArea={54}
heightTextArea="54px"
fontSize={13}
/>
<div className="property-comment_editor-editor-buttons">

View File

@ -64,6 +64,7 @@ import {
getCategoryUrl,
} from "SRC_DIR/helpers/utils";
import { getLogoFromPath } from "@docspace/shared/utils";
import TariffBar from "SRC_DIR/components/TariffBar";
const StyledContainer = styled.div`
width: 100%;
@ -209,6 +210,7 @@ const SectionHeaderContent = (props) => {
getAccountsCheckboxItemLabel,
setAccountsSelected,
isOwner,
isCollaborator,
setInvitePanelOptions,
isEmptyPage,
@ -776,8 +778,11 @@ const SectionHeaderContent = (props) => {
label: t("Files:CreateRoom"),
key: "create-room",
icon: CatalogRoomsReactSvgUrl,
onClick: onClickCreateRoom,
disabled: selectedFolder.rootFolderType !== FolderType.USER,
onClick: () => {
onClickCreateRoom({ title: selectedFolder.title, isFolder: true });
},
disabled:
isCollaborator || selectedFolder.rootFolderType !== FolderType.USER,
},
{
id: "option_leave-room",
@ -1108,6 +1113,7 @@ const SectionHeaderContent = (props) => {
showRootFolderTitle={insideTheRoom}
currentDeviceType={currentDeviceType}
isFrame={isFrame}
tariffBar={<TariffBar />}
/>
</div>
)}
@ -1135,6 +1141,7 @@ export default inject(
}) => {
const isOwner = auth.userStore.user?.isOwner;
const isAdmin = auth.userStore.user?.isAdmin;
const isCollaborator = auth?.userStore?.user?.isCollaborator;
const {
setSelected,
@ -1397,6 +1404,7 @@ export default inject(
setAccountsSelected,
isOwner,
isAdmin,
isCollaborator,
setInvitePanelOptions,
isEmptyPage,

View File

@ -11,7 +11,7 @@ import { IconButton } from "@docspace/shared/components/icon-button";
import { TableGroupMenu } from "@docspace/shared/components/table";
import { DropDownItem } from "@docspace/shared/components/drop-down-item";
import LoaderSectionHeader from "../loaderSectionHeader";
import { tablet, desktop } from "@docspace/shared/utils";
import { mobile, tablet, desktop } from "@docspace/shared/utils";
import withLoading from "SRC_DIR/HOCs/withLoading";
import { Badge } from "@docspace/shared/components/badge";
import {
@ -21,6 +21,7 @@ import {
checkPropertyByLink,
} from "../../../utils";
import { combineUrl } from "@docspace/shared/utils/combineUrl";
import TariffBar from "SRC_DIR/components/TariffBar";
const HeaderContainer = styled.div`
position: relative;
@ -105,6 +106,24 @@ const HeaderContainer = styled.div`
line-height: 59px !important;
}
}
@media ${mobile} {
h1 {
line-height: 53px;
font-size: ${(props) => props.theme.getCorrectFontSize("18px")};
}
}
.tariff-bar {
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-right: auto;
`
: css`
margin-left: auto;
`}
}
`;
const StyledContainer = styled.div`
@ -326,6 +345,10 @@ const SectionHeaderContent = (props) => {
)}
</div>
</Headline>
<div className="tariff-bar">
<TariffBar />
</div>
{props.addUsers && (
<div className="action-wrapper">
<IconButton

View File

@ -153,7 +153,7 @@ const Frame = styled(Box)`
border: 1px solid #d0d5da;
width: ${(props) => (props.width ? props.width : "100%")};
height: ${(props) => (props.height ? props.height : "400px")};
height: calc(${(props) => (props.height ? props.height : "400px")} + 2px);
@media ${tablet} {
margin-top: 4px;
@ -508,7 +508,7 @@ const PortalIntegration = (props) => {
<CategorySubHeader className="copy-window-code">
{t("CopyWindowCode")}
</CategorySubHeader>
<Textarea value={codeBlock} heightTextArea={153} />
<Textarea value={codeBlock} heightTextArea="153px" />
</>
);

View File

@ -27,7 +27,7 @@ const GetCodeDialog = (props) => {
<ModalDialog visible={visible} isLarge onClose={onClose}>
<ModalDialog.Header>{t("CopyWindowCode")}</ModalDialog.Header>
<ModalDialog.Body>
<StyledTextarea isReadOnly heightTextArea={180} value={codeBlock} />
<StyledTextarea isReadOnly heightTextArea="180px" value={codeBlock} />
</ModalDialog.Body>
<ModalDialog.Footer>
<Button

View File

@ -4,17 +4,16 @@ import { tablet } from "@docspace/shared/utils";
export const StyledHeader = styled.div`
position: relative;
display: grid;
grid-template-columns: ${(props) =>
props.showContextButton ? "auto auto auto 1fr" : "auto 1fr"};
display: flex;
align-items: center;
@media ${tablet} {
grid-template-columns: ${(props) =>
props.showContextButton ? "auto 1fr auto" : "auto 1fr"};
}
.action-button {
width: 100%;
display: flex;
gap: 16px;
align-items: center;
flex-direction: row;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
@ -24,6 +23,7 @@ export const StyledHeader = styled.div`
margin-left: 16px;
`}
@media ${tablet} {
flex-direction: row-reverse;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
@ -45,6 +45,17 @@ export const StyledHeader = styled.div`
`}
}
}
.tariff-bar {
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-right: auto;
`
: css`
margin-left: auto;
`}
}
}
.arrow-button {
${(props) =>
@ -60,6 +71,7 @@ export const StyledHeader = styled.div`
}
.header-headline {
white-space: nowrap;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`

View File

@ -19,6 +19,7 @@ import { DeleteOwnerProfileDialog } from "SRC_DIR/components/dialogs";
import { StyledHeader } from "./StyledHeader";
import RoomsFilter from "@docspace/shared/api/rooms/filter";
import { RoomSearchArea } from "@docspace/shared/enums";
import TariffBar from "SRC_DIR/components/TariffBar";
const Header = (props) => {
const {
@ -139,13 +140,13 @@ const Header = (props) => {
className="arrow-button"
/>
<Headline className="header-headline" type="content" truncate={true}>
<Headline className="header-headline" type="content">
{t("Profile:MyProfile")}
{profile?.isLDAP && ` (${t("PeopleTranslations:LDAPLbl")})`}
</Headline>
<div className="action-button">
{((isAdmin && !profile?.isOwner) || isMe) && (
<ContextMenuButton
className="action-button"
directionX="right"
title={t("Common:Actions")}
iconName={VerticalDotsReactSvgUrl}
@ -156,6 +157,11 @@ const Header = (props) => {
/>
)}
<div className="tariff-bar">
<TariffBar />
</div>
</div>
{deleteSelfProfileDialog && (
<DeleteSelfProfileDialog
visible={deleteSelfProfileDialog}

View File

@ -1,11 +1,16 @@
import React from "react";
import React, { useEffect } from "react";
import { withTranslation } from "react-i18next";
import { frameCallCommand } from "@docspace/shared/utils/common";
import ErrorContainer from "@docspace/common/components/ErrorContainer";
const RoomErrors = ({ t, tReady, isInvalid }) => {
const headerText = isInvalid ? t("InvalidLink") : t("Common:ExpiredLink");
const bodyText = isInvalid ? t("LinkDoesNotExist") : t("LinkHasExpired");
useEffect(() => {
frameCallCommand("setIsLoaded");
}, []);
return tReady ? (
<ErrorContainer headerText={headerText} bodyText={bodyText} />
) : (

View File

@ -7,6 +7,7 @@ import { FieldContainer } from "@docspace/shared/components/field-container";
import { inject, observer } from "mobx-react";
import { StyledPage, StyledBody, StyledContent } from "./RoomStyles";
// import { createPasswordHash } from "@docspace/shared/utils/common";
import { frameCallCommand } from "@docspace/shared/utils/common";
import { toastr } from "@docspace/shared/components/toast";
import { FormWrapper } from "@docspace/shared/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
@ -27,6 +28,8 @@ const RoomPassword = (props) => {
const inputRef = useRef(null);
useEffect(() => frameCallCommand("setIsLoaded"), []);
useEffect(() => {
if (inputRef && inputRef.current) {
inputRef.current.focus();

View File

@ -274,7 +274,7 @@ const VersionRow = (props) => {
className="version_edit-comment"
onChange={onChange}
fontSize={12}
heightTextArea={54}
heightTextArea="54px"
value={commentValue}
isDisabled={isSavingComment}
autoFocus={true}

View File

@ -1529,10 +1529,14 @@ class ContextOptionsStore {
});
}
const { isCollaborator } = this.authStore?.userStore?.user || { isCollaborator: false };
const { isCollaborator } = this.authStore?.userStore?.user || {
isCollaborator: false,
};
const newOptions = options.filter(
(option, index) => !(index === 0 && option.key === "separator1") && !(isCollaborator && option.key === "create-room")
(option, index) =>
!(index === 0 && option.key === "separator1") &&
!(isCollaborator && option.key === "create-room")
);
return newOptions;
@ -1785,10 +1789,14 @@ class ContextOptionsStore {
},
];
const { isCollaborator } = this.authStore?.userStore?.user || { isCollaborator: false };
const { isCollaborator } = this.authStore?.userStore?.user || {
isCollaborator: false,
};
const newOptions = options.filter(
(option, index) => !(index === 0 && option.key === "separator1") && !(isCollaborator && option.key === "create-room")
(option, index) =>
!(index === 0 && option.key === "separator1") &&
!(isCollaborator && option.key === "create-room")
);
return newOptions;

View File

@ -1606,9 +1606,12 @@ class FilesActionStore {
return !allFilesIsEditing && canDelete && hasSelection;
case "create-room":
const { isCollaborator } = this.authStore?.userStore?.user || { isCollaborator: false };
const { isCollaborator } = this.authStore?.userStore?.user || {
isCollaborator: false,
};
const canCreateRoom = !isCollaborator && rootFolderType === FolderType.USER;
const canCreateRoom =
!isCollaborator && rootFolderType === FolderType.USER;
return canCreateRoom;
}
@ -1746,9 +1749,12 @@ class FilesActionStore {
this.processCreatingRoomFromData = processCreatingRoomFromData;
};
onClickCreateRoom = () => {
onClickCreateRoom = (item) => {
this.setProcessCreatingRoomFromData(true);
const event = new Event(Events.ROOM_CREATE);
if (item && item.isFolder) {
event.title = item.title;
}
window.dispatchEvent(event);
};

View File

@ -19,6 +19,7 @@ import { ZendeskAPI } from "@docspace/shared/components/zendesk";
import { LIVE_CHAT_LOCAL_STORAGE_KEY } from "@docspace/shared/constants";
import { toastr } from "@docspace/shared/components/toast";
import { isDesktop, isTablet } from "@docspace/shared/utils";
import TariffBar from "SRC_DIR/components/TariffBar";
const PROXY_HOMEPAGE_URL = combineUrl(window.DocSpaceConfig?.proxy?.url, "/");
const PROFILE_SELF_URL = combineUrl(PROXY_HOMEPAGE_URL, "/profile");
@ -332,6 +333,7 @@ class ProfileActionsStore {
icon: PaymentsReactSvgUrl,
label: t("Common:PaymentsTitle"),
onClick: this.onPaymentsClick,
additionalElement: <TariffBar />,
},
{
isSeparator: true,

View File

@ -180,7 +180,7 @@ const RecoverAccessModalDialog: React.FC<IRecoverAccessModalDialogProps> = ({
value={description}
onChange={onChangeDescription}
isDisabled={loading}
heightTextArea={70}
heightTextArea="70px"
/>
</FieldContainer>
</ModalDialog.Body>

View File

@ -23,8 +23,8 @@ import {
import { EditorWrapper } from "../components/StyledEditor";
import { useTranslation } from "react-i18next";
import withDialogs from "../helpers/withDialogs";
import { assign, frameCallEvent } from "@docspace/shared/utils/common";
import { getEditorTheme, frameCallCommand } from "@docspace/shared/utils";
import { assign, frameCallEvent, frameCallCommand } from "@docspace/shared/utils/common";
import { getEditorTheme } from "@docspace/shared/utils";
import { toastr } from "@docspace/shared/components/toast";
import { DocumentEditor } from "@onlyoffice/document-editor-react";
import ErrorContainer from "@docspace/common/components/ErrorContainer";

View File

@ -9,7 +9,7 @@ import { ReactSVG } from "react-svg";
import { LoginFormWrapper } from "./StyledLogin";
import BarLogo from "PUBLIC_DIR/images/danger.alert.react.svg";
import { Dark, Base } from "@docspace/shared/themes";
import { getBgPattern } from "@docspace/shared/utils/common";
import { getBgPattern, frameCallCommand } from "@docspace/shared/utils/common";
import { getLogoFromPath } from "@docspace/shared/utils";
import { useMounted } from "../helpers/useMounted";
import useIsomorphicLayoutEffect from "../hooks/useIsomorphicLayoutEffect";
@ -57,6 +57,7 @@ const Form: React.FC<ILoginProps> = ({ theme, setTheme, logoUrls }) => {
? Dark
: Base;
setTheme(theme);
frameCallCommand("setIsLoaded");
}, []);
useIsomorphicLayoutEffect(() => {

View File

@ -22,7 +22,7 @@ import { ColorTheme, ThemeId } from "@docspace/shared/components/color-theme";
import SSOIcon from "PUBLIC_DIR/images/sso.react.svg";
import { Dark, Base } from "@docspace/shared/themes";
import { useMounted } from "../helpers/useMounted";
import { getBgPattern } from "@docspace/shared/utils/common";
import { getBgPattern, frameCallCommand } from "@docspace/shared/utils/common";
import useIsomorphicLayoutEffect from "../hooks/useIsomorphicLayoutEffect";
import { getLogoFromPath, getSystemTheme } from "@docspace/shared/utils";
import { TenantStatus } from "@docspace/shared/enums";
@ -82,6 +82,7 @@ const Login: React.FC<ILoginProps> = ({
const systemTheme = getSystemTheme();
const theme = themes[systemTheme];
setTheme(theme);
frameCallCommand("setIsLoaded");
}, []);
const ssoExists = () => {

View File

@ -74,24 +74,11 @@ export interface ArticleAlertsProps {
articleAlertsData?: { current: string; available: string[] };
incrementIndexOfArticleAlertsData?: () => void;
showText?: boolean;
isNonProfit?: boolean;
isGracePeriod?: boolean;
isFreeTariff?: boolean;
isPaymentPageAvailable?: boolean;
isTeamTrainingAlertAvailable?: boolean;
isSubmitToGalleryAlertAvailable?: boolean;
isLicenseExpiring?: boolean;
isLicenseDateExpired?: boolean;
isEnterprise?: boolean;
isTrial?: boolean;
standalone?: boolean;
currentTariffPlanTitle?: string;
toggleArticleOpen?: TToggleArticleOpen;
bookTrainingEmail?: string;
removeAlertFromArticleAlertsData?: TRemoveAlert;
setSubmitToGalleryDialogVisible?: (value: boolean) => void;
trialDaysLeft?: number;
paymentDate?: Date;
}
export interface ArticleAppsProps {

View File

@ -280,30 +280,17 @@ const Article = ({
incrementIndexOfArticleAlertsData={
incrementIndexOfArticleAlertsData
}
isNonProfit={isNonProfit}
isEnterprise={isEnterprise}
isFreeTariff={isFreeTariff}
isGracePeriod={isGracePeriod}
isLicenseDateExpired={isLicenseDateExpired}
isLicenseExpiring={isLicenseExpiring}
isPaymentPageAvailable={isPaymentPageAvailable}
isSubmitToGalleryAlertAvailable={
isSubmitToGalleryAlertAvailable
}
isTeamTrainingAlertAvailable={isTeamTrainingAlertAvailable}
isTrial={isTrial}
standalone={standalone}
bookTrainingEmail={bookTrainingEmail}
removeAlertFromArticleAlertsData={
removeAlertFromArticleAlertsData
}
currentTariffPlanTitle={currentTariffPlanTitle}
toggleArticleOpen={toggleArticleOpen}
setSubmitToGalleryDialogVisible={
setSubmitToGalleryDialogVisible
}
trialDaysLeft={trialDaysLeft}
paymentDate={paymentDate}
/>
)}
{withDevTools && (

View File

@ -4,8 +4,6 @@ import { ArticleAlerts } from "../../../enums";
import ArticleTeamTrainingAlert from "./TeamTrainingAlert";
import ArticleSubmitToFormGalleryAlert from "./SubmitToFormGalleryAlert";
import ArticlePaymentAlert from "./PaymentAlert";
import ArticleEnterpriseAlert from "./EnterpriseAlert";
import { ArticleAlertsProps } from "../Article.types";
import { StyledArticleAlertsComponent } from "../Article.styled";
@ -14,24 +12,11 @@ const ArticleAlertsComponent = ({
articleAlertsData,
incrementIndexOfArticleAlertsData,
showText,
isNonProfit,
isGracePeriod,
isFreeTariff,
isPaymentPageAvailable,
isTeamTrainingAlertAvailable,
isSubmitToGalleryAlertAvailable,
isLicenseExpiring,
isLicenseDateExpired,
isEnterprise,
isTrial,
standalone,
currentTariffPlanTitle,
toggleArticleOpen,
bookTrainingEmail,
removeAlertFromArticleAlertsData,
setSubmitToGalleryDialogVisible,
paymentDate,
trialDaysLeft,
}: ArticleAlertsProps) => {
const currentAlert = articleAlertsData?.current;
const availableAlerts = articleAlertsData?.available;
@ -40,46 +25,8 @@ const ArticleAlertsComponent = ({
incrementIndexOfArticleAlertsData?.();
}, [incrementIndexOfArticleAlertsData]);
const paymentsAlertsComponent = () => {
if (!standalone) {
return (
isPaymentPageAvailable &&
!isNonProfit &&
(isFreeTariff || isGracePeriod) &&
showText && (
<ArticlePaymentAlert
isFreeTariff={isFreeTariff}
currentTariffPlanTitle={currentTariffPlanTitle}
toggleArticleOpen={toggleArticleOpen}
/>
)
);
}
const isVisibleStandaloneAlert =
isTrial || isLicenseExpiring || isLicenseDateExpired;
return (
isPaymentPageAvailable &&
isEnterprise &&
isVisibleStandaloneAlert &&
showText && (
<ArticleEnterpriseAlert
toggleArticleOpen={toggleArticleOpen}
isLicenseDateExpired={isLicenseDateExpired}
trialDaysLeft={trialDaysLeft}
isTrial={isTrial}
paymentDate={paymentDate}
isEnterprise={isEnterprise}
/>
)
);
};
return (
<StyledArticleAlertsComponent>
{paymentsAlertsComponent()}
{isTeamTrainingAlertAvailable &&
showText &&
availableAlerts?.includes(ArticleAlerts.TeamTraining) &&

View File

@ -1,102 +0,0 @@
import React, { useState } from "react";
import { useTheme } from "styled-components";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { combineUrl } from "../../../utils/combineUrl";
import { RectangleSkeleton } from "../../../skeletons";
import AlertComponent from "../../alert";
import { ArticleEnterpriseAlertProps } from "../Article.types";
const PROXY_BASE_URL = combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings",
);
const ArticleEnterpriseAlert = ({
toggleArticleOpen,
isLicenseDateExpired,
trialDaysLeft,
isTrial,
paymentDate,
isEnterprise,
}: ArticleEnterpriseAlertProps) => {
const theme = useTheme();
const { t, ready } = useTranslation("Common");
const navigate = useNavigate();
const [isClose, setIsClose] = useState<string | boolean | null>(
localStorage.getItem("enterpriseAlertClose"),
);
const onCloseClick = () => {
localStorage.setItem("enterpriseAlertClose", "true");
setIsClose(true);
};
const onAlertClick = () => {
const paymentPageUrl = combineUrl(
PROXY_BASE_URL,
"/payments/portal-payments",
);
navigate(paymentPageUrl);
toggleArticleOpen?.();
};
const titleFunction = () => {
if (isTrial) {
if (isLicenseDateExpired) return t("Common:TrialExpired");
return t("Common:TrialDaysLeft", { count: trialDaysLeft });
}
return t("TariffEnterprise");
};
const descriptionFunction = () => {
if (isLicenseDateExpired) {
if (isTrial) return;
return t("Common:SubscriptionExpired");
}
return t("Common:SubscriptionIsExpiring", { date: paymentDate });
};
const title = titleFunction();
const additionalDescription = t("Common:RenewSubscription");
const description = descriptionFunction();
const color = isLicenseDateExpired
? theme.catalog.paymentAlert.warningColor
: theme.catalog.paymentAlert.color;
const isShowLoader = !ready;
if (isEnterprise && isClose) return null;
return isShowLoader ? (
<RectangleSkeleton width="210px" height="88px" />
) : (
<AlertComponent
id="document_catalog-payment-alert"
borderColor={color}
titleColor={color}
onAlertClick={onAlertClick}
onCloseClick={onCloseClick}
title={title}
titleFontSize="11px"
description={description}
additionalDescription={additionalDescription}
needArrowIcon={isTrial}
needCloseIcon={!isTrial}
/>
);
};
export default ArticleEnterpriseAlert;

View File

@ -1,75 +0,0 @@
import React from "react";
import { useTheme } from "styled-components";
import { useNavigate } from "react-router-dom";
import { useTranslation, Trans } from "react-i18next";
import { combineUrl } from "../../../utils/combineUrl";
import { RectangleSkeleton } from "../../../skeletons";
import AlertComponent from "../../alert";
import { ArticlePaymentAlertProps } from "../Article.types";
const PROXY_BASE_URL = combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings",
);
const ArticlePaymentAlert = ({
isFreeTariff,
currentTariffPlanTitle,
toggleArticleOpen,
}: ArticlePaymentAlertProps) => {
const { t, ready } = useTranslation("Common");
const theme = useTheme();
const navigate = useNavigate();
const onClick = () => {
const paymentPageUrl = combineUrl(
PROXY_BASE_URL,
"/payments/portal-payments",
);
navigate(paymentPageUrl);
toggleArticleOpen?.();
};
const title = isFreeTariff ? (
<Trans t={t} i18nKey="FreeStartupPlan" ns="Common">
{{ planName: currentTariffPlanTitle }}
</Trans>
) : (
t("Common:LatePayment")
);
const description = isFreeTariff
? t("Common:GetMoreOptions")
: t("Common:PayBeforeTheEndGracePeriod");
const additionalDescription = isFreeTariff
? t("Common:ActivatePremiumFeatures")
: t("Common:GracePeriodActivated");
const color = isFreeTariff
? theme.catalog.paymentAlert.color
: theme.catalog.paymentAlert.warningColor;
const isShowLoader = !ready;
return isShowLoader ? (
<RectangleSkeleton width="210px" height="88px" />
) : (
<AlertComponent
id="document_catalog-payment-alert"
borderColor={color}
titleColor={color}
onAlertClick={onClick}
title={title}
titleFontSize="11px"
description={description}
additionalDescription={additionalDescription}
needArrowIcon
/>
);
};
export default ArticlePaymentAlert;

View File

@ -261,4 +261,11 @@ const IconWrapper = styled.div`
`;
IconWrapper.defaultProps = { theme: Base };
export { StyledDropdownItem, IconWrapper, WrapperToggle, WrapperBadge };
const ElementWrapper = styled.div`
display: flex;
align-items: center;
${(props) =>
props.theme.interfaceDirection === "rtl" ? css`margin-right: auto;` : css`margin-left: auto;`}
`;
export { StyledDropdownItem, IconWrapper, WrapperToggle, WrapperBadge, ElementWrapper };

View File

@ -13,6 +13,7 @@ import {
IconWrapper,
WrapperToggle,
WrapperBadge,
ElementWrapper,
} from "./DropDownItem.styled";
import { DropDownItemProps } from "./DropDownItem.types";
@ -37,6 +38,7 @@ const DropDownItem = (props: DropDownItemProps) => {
isSelected,
isActiveDescendant,
isBeta,
additionalElement,
} = props;
const { t } = useTranslation(["Settings"]);
@ -143,6 +145,10 @@ const DropDownItem = (props: DropDownItemProps) => {
/>
</WrapperBadge>
)}
{additionalElement && (
<ElementWrapper>{additionalElement}</ElementWrapper>
)}
</StyledDropdownItem>
);
};
@ -164,3 +170,4 @@ DropDownItem.defaultProps = {
};
export { DropDownItem };

View File

@ -47,4 +47,6 @@ export interface DropDownItemProps {
checked?: boolean;
onClickSelectedItem?: () => void;
isBeta?: boolean;
additionalElement?: React.ReactNode;
}

View File

@ -24,10 +24,8 @@ const StyledContainer = styled.div<{
!props.isDropBoxComponent &&
props.isDesktop &&
css`
width: fit-content;
max-width: ${props.isInfoPanelVisible
? `calc(100%)`
: `calc(100% - 72px)`};
width: 100%;
max-width: 100%;
`}
display: grid;
@ -210,7 +208,7 @@ const StyledContainer = styled.div<{
}
grid-template-columns: ${(props) =>
props.isRootFolder ? "1fr auto" : "29px 1fr auto"};
props.isRootFolder ? "auto 1fr" : "29px auto 1fr"};
}
`;
@ -223,11 +221,11 @@ const StyledInfoPanelToggleColorThemeWrapper = styled(ColorTheme)<{
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-right: auto;
margin-right: 20px;
transform: scaleX(-1);
`
: css`
margin-left: auto;
margin-left: 20px;
`}
margin-bottom: 1px;
@ -278,18 +276,14 @@ const StyledControlButtonContainer = styled.div<{ isFrame?: boolean }>`
`}
display: flex;
align-items: center;
gap: 16px;
height: 32px;
@media ${tablet} {
flex-direction: row-reverse;
}
.add-button {
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-left: 16px;
`
: css`
margin-right: 16px;
`}
min-width: 15px;
@media ${tablet} {
@ -640,6 +634,26 @@ const StyledBox = styled.div<{
StyledBox.defaultProps = { theme: Base };
const StyledTariffWrapper = styled.div`
display: flex;
align-items: center;
${({ theme }) =>
theme.interfaceDirection === "rtl"
? css`
justify-content: left;
margin-right: auto;
`
: css`
justify-content: right;
margin-left: auto;
`}
@media ${tablet} {
flex-direction: row-reverse;
}
`;
export {
StyledContainer,
StyledInfoPanelToggleColorThemeWrapper,
@ -654,4 +668,5 @@ export {
StyledText,
StyledItem,
StyledBox,
StyledTariffWrapper,
};

View File

@ -52,6 +52,7 @@ const Navigation = ({
titleIcon,
currentDeviceType,
rootRoomTitle,
tariffBar,
...rest
}: INavigationProps) => {
@ -253,6 +254,8 @@ const Navigation = ({
isFrame={isFrame}
isPublicRoom={isPublicRoom}
isTrashFolder={isTrashFolder}
tariffBar={tariffBar}
title={title}
/>
</StyledContainer>
{isDesktop && isTrashFolder && !isEmptyPage && (

View File

@ -60,6 +60,8 @@ export interface IControlButtonProps {
isPublicRoom?: boolean;
isTrashFolder?: boolean;
isMobile?: boolean;
tariffBar?: React.ReactNode;
title?: string;
}
export interface ITextProps {
@ -160,4 +162,5 @@ export interface INavigationProps {
titleIcon: string;
currentDeviceType: DeviceType;
rootRoomTitle: string;
tariffBar: React.ReactNode;
}

View File

@ -1,6 +1,9 @@
import React from "react";
import { StyledControlButtonContainer } from "../Navigation.styled";
import {
StyledControlButtonContainer,
StyledTariffWrapper,
} from "../Navigation.styled";
import { IControlButtonProps } from "../Navigation.types";
import ToggleInfoPanelButton from "./ToggleInfoPanelBtn";
@ -24,11 +27,14 @@ const ControlButtons = ({
isPublicRoom,
isTrashFolder,
isMobile,
tariffBar,
title,
}: IControlButtonProps) => {
const toggleInfoPanelAction = () => {
toggleInfoPanel?.();
toggleDropBox?.();
};
const children = tariffBar ? React.cloneElement(tariffBar, { title }) : null;
return (
<StyledControlButtonContainer isFrame={isFrame}>
@ -124,8 +130,9 @@ const ControlButtons = ({
)}
</>
)}
<StyledTariffWrapper>{children && children}</StyledTariffWrapper>
</StyledControlButtonContainer>
);
};
export default React.memo(ControlButtons);
export default ControlButtons;

View File

@ -17,6 +17,10 @@ export interface ScrollbarProps {
noScrollY?: boolean;
/** Disable horizontal scrolling. */
noScrollX?: boolean;
/** Calculating height of content depending on number of lines */
isFullHeight?: boolean;
/** Calculated height of content depending on number of lines in pixels */
fullHeight?: number;
onScroll?: React.UIEventHandler<HTMLDivElement>;
scrollclass?: string;

View File

@ -20,7 +20,7 @@ import { Textarea } from "@docspace/shared/components/textarea";
### Properties
| Props | Type | Required | Values | Default | Description |
| ---------------- | :------------: | :------: | :----: | :--------------------------------: | -------------------------------------------------------- |
| ---------------- | :----------------: | :------: | :----: | :--------------------------------: | -------------------------------------------------------- |
| `className` | `string` | - | - | - | Class name |
| `id` | `string` | - | - | - | Used as HTML `id` property |
| `isDisabled` | `bool` | - | - | `false` | Indicates that the field cannot be used |
@ -32,6 +32,6 @@ import { Textarea } from "@docspace/shared/components/textarea";
| `style` | `obj`, `array` | - | - | - | Accepts css style |
| `value` | `string` | - | - | - | Value for Textarea |
| `fontSize` | `number` | - | - | 13 | Value for font-size |
| `heightTextArea` | `number` | - | - | - | Value for height text-area |
| `heightTextArea` | `string`, `number` | - | - | - | Value for height text-area |
| `isJSONField` | `bool` | - | - | `false` | Indicates that the field is displaying JSON object |
| `copyInfoText` | `string` | - | - | `Content was copied successfully!` | Indicates the text of toast/informational alarm |

View File

@ -51,7 +51,7 @@ export const Default: Story = {
name: "",
tabIndex: 1,
fontSize: 13,
heightTextArea: 89,
heightTextArea: "89px",
value:
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae",
isJSONField: false,
@ -80,7 +80,7 @@ export const JsonViewer: Story = {
name: "",
tabIndex: 1,
fontSize: 13,
heightTextArea: 89,
heightTextArea: "89px",
value:
'{"menu": {"id": "file","value": "File","popup": {"menuitem": [{"value": "New", "onclick": "CreateNewDoc()"},{"value": "Open", "onclick": "OpenDoc()"},{"value": "Close", "onclick": "CloseDoc()"}]}}}',
isJSONField: true,

View File

@ -15,12 +15,14 @@ const ClearScrollbar = ({
heightScale,
hasError,
heightTextAreaProp,
isFullHeight,
fullHeight,
...props
}: {
isDisabled?: boolean;
heightScale?: boolean;
hasError?: boolean;
heightTextAreaProp?: number;
heightTextAreaProp?: string;
ref?: React.Ref<HTMLDivElement>;
} & ScrollbarProps) => <Scrollbar {...props} />;
@ -37,20 +39,26 @@ const StyledScrollbar = styled(ClearScrollbar)`
}
width: ${(props) => props.theme.textArea.scrollWidth} !important;
height: ${(props) => {
height: calc(
${(props) => {
return props.heightScale
? "67vh"
: props.isFullHeight
? `${props.fullHeight}px`
: props.heightTextAreaProp
? `${props.heightTextAreaProp + 2}px`
? props.heightTextAreaProp
: "91px";
}} !important;
}} + 2px
) !important;
textarea {
height: ${(props) => {
return props.heightScale
? "65vh"
: props.isFullHeight
? `${props.fullHeight}px`
: props.heightTextAreaProp
? `${props.heightTextAreaProp}px`
? props.heightTextAreaProp
: "89px";
}};
}
@ -72,13 +80,11 @@ const ClearTextareaAutosize = React.forwardRef(
paddingLeftProp,
isJSONField,
enableCopy,
heightTextAreaProp,
heightTextArea,
...props
}: TextareaProps & {
disabled?: boolean;
readOnly?: boolean;
heightTextAreaProp?: number;
},
ref: React.Ref<HTMLTextAreaElement>,
) => <TextareaAutosize {...props} ref={ref} />,
@ -202,10 +208,26 @@ const CopyIconWrapper = styled.div<{
CopyIconWrapper.defaultProps = { theme: Base };
const Wrapper = styled.div<{ enableCopy?: boolean; isJSONField?: boolean }>`
const Wrapper = styled.div<{
heightScale?: boolean;
isFullHeight?: boolean;
fullHeight?: number;
heightTextArea?: string;
enableCopy?: boolean;
isJSONField?: boolean;
}>`
position: relative;
max-width: 1200px;
height: ${(props) => {
return props.heightScale
? "65vh"
: props.isFullHeight
? `${props.fullHeight}px`
: props.heightTextArea
? props.heightTextArea
: "89px";
}};
.scroll-wrapper {
margin-right: ${(props) =>
@ -216,8 +238,7 @@ const Wrapper = styled.div<{ enableCopy?: boolean; isJSONField?: boolean }>`
const Numeration = styled.pre<{ fontSize: string }>`
display: block;
position: absolute;
font-size: ${(props) => props.theme.getCorrectFontSize(props.fontSize)};
font-size: ${(props) => props.theme.getCorrectFontSize(props.fontSize)}px;
font-family: ${(props) => props.theme.fontFamily};
line-height: 1.5;
margin: 0;

View File

@ -71,12 +71,12 @@ describe("<Textarea />", () => {
// onChange={jest.fn()}
// value="value"
// className="test"
// heightTextArea={54}
// heightTextArea="54px"
// />
// );
// // @ts-expect-error TS(2304): Cannot find name 'expect'.
// expect(wrapper.prop("heightTextArea")).toEqual(54);
// expect(wrapper.prop("heightTextArea")).toEqual("54px");
// });
// // @ts-expect-error TS(2582): Cannot find name 'it'. Do you need to install type... Remove this comment to see the full error message

View File

@ -53,9 +53,9 @@ const Textarea = ({
const lineHeight = 1.5;
const padding = 7;
const numberOfLines = modifiedValue.split("\n").length;
const textareaHeight = isFullHeight
? numberOfLines * fontSize * lineHeight + padding + 4
: heightTextArea;
const fullHeight = numberOfLines * fontSize * lineHeight + padding + 4;
const stringifiedHeight =
typeof heightTextArea === "number" ? `${heightTextArea}px` : heightTextArea;
const defaultPaddingLeft = 42;
const numberOfDigits =
@ -77,8 +77,8 @@ const Textarea = ({
};
useEffect(() => {
if (hasError !== isError) setIsError(hasError);
}, [hasError, isError]);
setIsError(hasError);
}, [hasError]);
useEffect(() => {
setIsError(isJSONField && (!value || !isJSON(value)));
@ -97,6 +97,10 @@ const Textarea = ({
enableCopy={enableCopy}
onClick={onTextareaClick}
data-testid="textarea"
heightScale={heightScale}
heightTextArea={stringifiedHeight}
isFullHeight={isFullHeight}
fullHeight={fullHeight}
>
{enableCopy && (
<CopyIconWrapper
@ -118,7 +122,9 @@ const Textarea = ({
isDisabled={isDisabled}
hasError={isError}
heightScale={heightScale}
heightTextAreaProp={textareaHeight}
heightTextAreaProp={stringifiedHeight}
isFullHeight={isFullHeight}
fullHeight={fullHeight}
>
<Toast />
@ -128,7 +134,6 @@ const Textarea = ({
</Numeration>
)}
<StyledTextarea
heightTextAreaProp={textareaHeight}
id={id}
paddingLeftProp={paddingLeftProp}
isJSONField={isJSONField}

View File

@ -31,7 +31,7 @@ export interface TextareaProps {
/** Font-size value */
fontSize?: number;
/** Text-area height value */
heightTextArea?: number;
heightTextArea?: string | number;
/** Specifies the text color */
color?: string;
/** Default input property */
@ -48,13 +48,16 @@ export interface TextareaProps {
hasNumeration?: boolean;
/** Calculating height of content depending on number of lines */
isFullHeight?: boolean;
/** Calculated height of content depending on number of lines in pixels */
fullHeight?: number;
classNameCopyIcon?: string;
paddingLeftProp?: string;
}
export interface TextareaThemeProps extends TextareaProps {
ref: React.LegacyRef<HTMLTextAreaElement>;
heightTextAreaProp?: number;
heightTextAreaProp?: string;
$currentColorScheme?: TColorScheme;
interfaceDirection?: string;
}

View File

@ -300,6 +300,7 @@
"Standard": "Standard",
"SubmitToFormGallery": "Submit to Form Gallery",
"SubmitToGallery": "Submit to Gallery",
"SubscriptionExpiredTitle": "Subscription expired",
"SubscriptionExpired": "Your subscription to support and updates has expired",
"SubscriptionIsExpiring": "Your subscription is expiring on {{date}}",
"Support": "Support",
@ -314,6 +315,7 @@
"Today": "Today",
"TrialDaysLeft": "Trial {{count}} days",
"TrialExpired": "Trial expired",
"TryBusiness": "Try Business",
"Type": "Type",
"UTC": "UTC",
"UnexpectedError": "An unexpected error occurred. Try again later or contact support.",

View File

@ -474,7 +474,9 @@
target.innerHTML = this.config.destroyText;
target.className = this.#classNames;
const targetFrame = document.getElementById(this.config.frameId);
const targetFrame = document.getElementById(
this.config.frameId + "-container"
);
if (targetFrame) {
window.removeEventListener("message", this.#onMessage, false);