From 2caa55b7f429c9bc8fe965a19b1099d78cdd4061 Mon Sep 17 00:00:00 2001 From: Mikhail Gorbunov Date: Thu, 28 May 2026 17:19:51 +0500 Subject: [PATCH 1/5] feat(condo): hide user menu on feature flag --- .../common/components/containers/BaseLayout/Header.tsx | 6 +++++- .../containers/BaseLayout/components/TopMenuItems.tsx | 5 +++-- apps/condo/domains/common/constants/featureflags.js | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx b/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx index 60dcc44c8a8..2d4f56e96ac 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx @@ -24,7 +24,9 @@ import { ACCEPT_OR_REJECT_ORGANIZATION_INVITE_BY_ID_MUTATION } from '@condo/doma import { useOrganizationEmployeeRequests } from '@condo/domains/organization/hooks/useOrganizationEmployeeRequests' import { useOrganizationInvites } from '@condo/domains/organization/hooks/useOrganizationInvites' import { useOrganizationSubscription } from '@condo/domains/subscription/hooks' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { UserMenu } from '@condo/domains/user/components/UserMenu' +import { UI_HIDE_USER_HEADER_MENU_ITEM } from '@condo/domains/common/constants/featureflags' import { ITopMenuItemsProps, TopMenuItems } from './components/TopMenuItems' @@ -39,6 +41,8 @@ export const Header: React.FC = (props) => { const client = useApolloClient() const { toggleCollapsed, isMobileView } = useLayoutContext() const router = useRouter() + const { useFlag } = useFeatureFlags() + const isUserMenuHidden = useFlag(UI_HIDE_USER_HEADER_MENU_ITEM) const { isAuthenticated } = useAuth() const { organization } = useOrganization() @@ -89,7 +93,7 @@ export const Header: React.FC = (props) => { - + {!isUserMenuHidden && }
diff --git a/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx b/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx index 22d617a17f5..868a3127eeb 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx @@ -8,7 +8,7 @@ import { Space, Typography } from '@open-condo/ui' import { useAIContext } from '@condo/domains/ai/components/AIContext' import { AIFlowButton } from '@condo/domains/ai/components/AIFlowButton' -import { UI_AI_CHAT_WITH_CONDO } from '@condo/domains/common/constants/featureflags' +import { UI_AI_CHAT_WITH_CONDO, UI_HIDE_USER_HEADER_MENU_ITEM } from '@condo/domains/common/constants/featureflags' import { analytics } from '@condo/domains/common/utils/analytics' import { UserMessagesList } from '@condo/domains/notification/components/UserMessagesList' import { InlineOrganizationSelect } from '@condo/domains/organization/components/OrganizationSelect' @@ -32,6 +32,7 @@ export const TopMenuItems: React.FC = (props) => { const { useFlag } = useFeatureFlags() const { isAIOverlayOpen, openAIOverlay } = useAIContext() const isAIChatEnabled = useFlag(UI_AI_CHAT_WITH_CONDO) + const isUserMenuHidden = useFlag(UI_HIDE_USER_HEADER_MENU_ITEM) const PaymentHistoryLabel = intl.formatMessage({ id: 'subscription.paymentHistory.title' }) const LinkedCardsLabel = intl.formatMessage({ id: 'subscription.linkedCards.title' }) @@ -69,7 +70,7 @@ export const TopMenuItems: React.FC = (props) => { - + {!isUserMenuHidden && }
diff --git a/apps/condo/domains/common/constants/featureflags.js b/apps/condo/domains/common/constants/featureflags.js index 2d372b2d7c6..e751251e300 100644 --- a/apps/condo/domains/common/constants/featureflags.js +++ b/apps/condo/domains/common/constants/featureflags.js @@ -54,6 +54,7 @@ const NEWS_ITEM_FILES_MINIAPPS = 'news-item-files-miniapps' const NEWS_ITEM_FILE_PUBLISHING_TIMEOUT_BY_NEWS_ITEM = 'news-item-file-publishing-timeout-by-news-item' const CUSTOM_TOUR_STEPS = 'custom-tour-steps' const UI_LEGAL_INFO = 'ui-legal-info' +const UI_HIDE_USER_HEADER_MENU_ITEM = 'ui-hide-user-header-menu-item' module.exports = { SMS_AFTER_TICKET_CREATION, @@ -112,4 +113,5 @@ module.exports = { NEWS_ITEM_FILE_PUBLISHING_TIMEOUT_BY_NEWS_ITEM, CUSTOM_TOUR_STEPS, UI_LEGAL_INFO, + UI_HIDE_USER_HEADER_MENU_ITEM, } From 36e7cbe719475735256efc1d5af7bb313f0f11af Mon Sep 17 00:00:00 2001 From: Mikhail Gorbunov Date: Thu, 28 May 2026 18:41:16 +0500 Subject: [PATCH 2/5] feat(condo): hide layout components on feature flag --- .../containers/BaseLayout/BaseLayout.tsx | 11 +++++-- .../containers/BaseLayout/Footer.tsx | 32 ++++++++++--------- .../containers/BaseLayout/Header.tsx | 6 ++-- .../BaseLayout/components/TopMenuItems.tsx | 4 +-- .../domains/common/constants/featureflags.js | 8 +++-- .../domains/common/hooks/useGlobalHints.tsx | 14 ++++++-- apps/condo/lang/en/en.json | 6 ++-- apps/condo/pages/_app.tsx | 6 ++-- 8 files changed, 54 insertions(+), 33 deletions(-) diff --git a/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx b/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx index 00ae3e2e8a8..2184d27c89d 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx @@ -6,6 +6,8 @@ import React, { CSSProperties, FunctionComponent, ElementType } from 'react' import { useAIContext } from '@condo/domains/ai/components/AIContext' import { useLayoutContext } from '@condo/domains/common/components/LayoutContext' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' +import { UI_HIDE_LAYOUT_HEADER, UI_HIDE_LAYOUT_SIDE_MENU } from '@condo/domains/common/constants/featureflags' import styles from './BaseLayout.module.css' import { SideNav } from './components/SideNav' @@ -38,6 +40,9 @@ const BaseLayout: React.FC> = (props) const { isCollapsed } = useLayoutContext() const { isAIOverlayOpen, aiOverlayWidth } = useAIContext() + const { useFlag } = useFeatureFlags() + const isHeaderHidden = useFlag(UI_HIDE_LAYOUT_HEADER) + const isSideMenuHidden = useFlag(UI_HIDE_LAYOUT_SIDE_MENU) const subLayoutStyle: CSSProperties = { ...style, @@ -46,15 +51,15 @@ const BaseLayout: React.FC> = (props) return ( - - } + -
+ {!isHeaderHidden &&
} {children}
diff --git a/apps/condo/domains/common/components/containers/BaseLayout/Footer.tsx b/apps/condo/domains/common/components/containers/BaseLayout/Footer.tsx index 3981542ab37..a6dadc7eb98 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/Footer.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/Footer.tsx @@ -27,24 +27,26 @@ export const Footer: React.FC = () => { const localizedFooterConfig = footerConfig?.[intl?.locale] || null - return isLegalInfoEnabled && localizedFooterConfig && ( + return ( - - { - localizedFooterConfig?.privacyPolicyLink && - - - {privacyPolicyText} - - - } - + {isLegalInfoEnabled && localizedFooterConfig && ( + + { + localizedFooterConfig?.privacyPolicyLink && + + + {privacyPolicyText} + + + } + + )} ) } diff --git a/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx b/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx index 2d4f56e96ac..0dce37d050e 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/Header.tsx @@ -6,6 +6,7 @@ import get from 'lodash/get' import { useRouter } from 'next/router' import React, { useCallback, useMemo } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { Menu } from '@open-condo/icons' import { useMutation } from '@open-condo/next/apollo' import { useAuth } from '@open-condo/next/auth' @@ -15,6 +16,7 @@ import { Space } from '@open-condo/ui' import { useLayoutContext } from '@condo/domains/common/components/LayoutContext' import { Logo } from '@condo/domains/common/components/Logo' import { ResidentActions } from '@condo/domains/common/components/ResidentActions/ResidentActions' +import { UI_HIDE_USER_LINKS } from '@condo/domains/common/constants/featureflags' import { UserMessagesList } from '@condo/domains/notification/components/UserMessagesList' import { UserMessagesListContextProvider } from '@condo/domains/notification/contexts/UserMessagesListContext' import { InlineOrganizationSelect } from '@condo/domains/organization/components/OrganizationSelect' @@ -24,9 +26,7 @@ import { ACCEPT_OR_REJECT_ORGANIZATION_INVITE_BY_ID_MUTATION } from '@condo/doma import { useOrganizationEmployeeRequests } from '@condo/domains/organization/hooks/useOrganizationEmployeeRequests' import { useOrganizationInvites } from '@condo/domains/organization/hooks/useOrganizationInvites' import { useOrganizationSubscription } from '@condo/domains/subscription/hooks' -import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { UserMenu } from '@condo/domains/user/components/UserMenu' -import { UI_HIDE_USER_HEADER_MENU_ITEM } from '@condo/domains/common/constants/featureflags' import { ITopMenuItemsProps, TopMenuItems } from './components/TopMenuItems' @@ -42,7 +42,7 @@ export const Header: React.FC = (props) => { const { toggleCollapsed, isMobileView } = useLayoutContext() const router = useRouter() const { useFlag } = useFeatureFlags() - const isUserMenuHidden = useFlag(UI_HIDE_USER_HEADER_MENU_ITEM) + const isUserMenuHidden = useFlag(UI_HIDE_USER_LINKS) const { isAuthenticated } = useAuth() const { organization } = useOrganization() diff --git a/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx b/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx index 868a3127eeb..b35694506d3 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/components/TopMenuItems.tsx @@ -8,7 +8,7 @@ import { Space, Typography } from '@open-condo/ui' import { useAIContext } from '@condo/domains/ai/components/AIContext' import { AIFlowButton } from '@condo/domains/ai/components/AIFlowButton' -import { UI_AI_CHAT_WITH_CONDO, UI_HIDE_USER_HEADER_MENU_ITEM } from '@condo/domains/common/constants/featureflags' +import { UI_AI_CHAT_WITH_CONDO, UI_HIDE_USER_LINKS } from '@condo/domains/common/constants/featureflags' import { analytics } from '@condo/domains/common/utils/analytics' import { UserMessagesList } from '@condo/domains/notification/components/UserMessagesList' import { InlineOrganizationSelect } from '@condo/domains/organization/components/OrganizationSelect' @@ -32,7 +32,7 @@ export const TopMenuItems: React.FC = (props) => { const { useFlag } = useFeatureFlags() const { isAIOverlayOpen, openAIOverlay } = useAIContext() const isAIChatEnabled = useFlag(UI_AI_CHAT_WITH_CONDO) - const isUserMenuHidden = useFlag(UI_HIDE_USER_HEADER_MENU_ITEM) + const isUserMenuHidden = useFlag(UI_HIDE_USER_LINKS) const PaymentHistoryLabel = intl.formatMessage({ id: 'subscription.paymentHistory.title' }) const LinkedCardsLabel = intl.formatMessage({ id: 'subscription.linkedCards.title' }) diff --git a/apps/condo/domains/common/constants/featureflags.js b/apps/condo/domains/common/constants/featureflags.js index e751251e300..fb2ab037aaa 100644 --- a/apps/condo/domains/common/constants/featureflags.js +++ b/apps/condo/domains/common/constants/featureflags.js @@ -54,7 +54,9 @@ const NEWS_ITEM_FILES_MINIAPPS = 'news-item-files-miniapps' const NEWS_ITEM_FILE_PUBLISHING_TIMEOUT_BY_NEWS_ITEM = 'news-item-file-publishing-timeout-by-news-item' const CUSTOM_TOUR_STEPS = 'custom-tour-steps' const UI_LEGAL_INFO = 'ui-legal-info' -const UI_HIDE_USER_HEADER_MENU_ITEM = 'ui-hide-user-header-menu-item' +const UI_HIDE_USER_LINKS = 'ui-hide-user-links' +const UI_HIDE_LAYOUT_HEADER = 'ui-hide-layout-header' +const UI_HIDE_LAYOUT_SIDE_MENU = 'ui-hide-layout-sideMenu' module.exports = { SMS_AFTER_TICKET_CREATION, @@ -113,5 +115,7 @@ module.exports = { NEWS_ITEM_FILE_PUBLISHING_TIMEOUT_BY_NEWS_ITEM, CUSTOM_TOUR_STEPS, UI_LEGAL_INFO, - UI_HIDE_USER_HEADER_MENU_ITEM, + UI_HIDE_USER_LINKS, + UI_HIDE_LAYOUT_HEADER, + UI_HIDE_LAYOUT_SIDE_MENU, } diff --git a/apps/condo/domains/common/hooks/useGlobalHints.tsx b/apps/condo/domains/common/hooks/useGlobalHints.tsx index ee87ebbe49c..7cbf9f53873 100644 --- a/apps/condo/domains/common/hooks/useGlobalHints.tsx +++ b/apps/condo/domains/common/hooks/useGlobalHints.tsx @@ -11,11 +11,15 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { CSSProperties, useCallback, useEffect, useMemo, useState } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useAuth } from '@open-condo/next/auth' import { useIntl } from '@open-condo/next/intl' import { Banner, Carousel, Typography } from '@open-condo/ui' import { colors } from '@open-condo/ui/colors' +import { UI_HIDE_USER_LINKS } from '@condo/domains/common/constants/featureflags' + + interface HintI18n { title: string @@ -43,6 +47,8 @@ const HINTS_WRAPPER_STYLE: CSSProperties = { marginBottom: 40 } const HINTS_ROW_GUTTERS: RowProps['gutter'] = [0, 8] export const useGlobalHints = () => { + const { useFlag } = useFeatureFlags() + const isUserLinksHidden = useFlag(UI_HIDE_USER_LINKS) const intl = useIntl() const CanDisableHintsMessage = intl.formatMessage({ id: 'global.globalHints.canDisableHints' }) const InProfileMessage = intl.formatMessage({ id: 'global.globalHints.canDisableHints.inProfile' }) @@ -134,9 +140,11 @@ export const useGlobalHints = () => { {CanDisableHintsMessage}   - - {InProfileMessage} - + {!isUserLinksHidden && ( + + {InProfileMessage} + + )} diff --git a/apps/condo/lang/en/en.json b/apps/condo/lang/en/en.json index 8117290d1f7..932ac70f329 100644 --- a/apps/condo/lang/en/en.json +++ b/apps/condo/lang/en/en.json @@ -2544,9 +2544,9 @@ "pages.condo.property.field.Address.notFound.modal.contacts.description": "Choose the most convenient option for you:", "pages.condo.property.field.Address.notFound.modal.contacts.description.bot": "Telegram-bot", "pages.condo.property.field.Address.notFound.modal.contacts.description.email": "An email", - "pages.condo.property.field.Address.notFound.modal.contacts.title": "Write in support", - "pages.condo.property.field.Address.notFound.modal.message.button": "Write in support", - "pages.condo.property.field.Address.notFound.modal.message.description": "This happens if the house was built no more than 60 days ago - it has not yet been added to fias. Tell us the address, and we’ll fast-track it to the address information system.", + "pages.condo.property.field.Address.notFound.modal.contacts.title": "Write to support", + "pages.condo.property.field.Address.notFound.modal.message.button": "Write to support", + "pages.condo.property.field.Address.notFound.modal.message.description": "This happens if the house was built no more than 60 days ago - it has not yet been added to the government registry. Tell us the address, and we’ll fast-track it to the address information system.", "pages.condo.property.field.Address.notFound.modal.message.title": "No such address", "pages.condo.property.field.Name": "Name", "pages.condo.property.field.Organization": "Organization", diff --git a/apps/condo/pages/_app.tsx b/apps/condo/pages/_app.tsx index a1577449315..6094f929092 100644 --- a/apps/condo/pages/_app.tsx +++ b/apps/condo/pages/_app.tsx @@ -54,7 +54,7 @@ import { SurveysQueue } from '@condo/domains/common/components/surveys/SurveyQue import { TasksContextProvider } from '@condo/domains/common/components/tasks/TasksContextProvider' import UseDeskWidget from '@condo/domains/common/components/UseDeskWidget' import { COOKIE_MAX_AGE_IN_SEC } from '@condo/domains/common/constants/cookies' -import { SERVICE_PROVIDER_PROFILE } from '@condo/domains/common/constants/featureflags' +import { SERVICE_PROVIDER_PROFILE, UI_LEGAL_INFO } from '@condo/domains/common/constants/featureflags' import { TOUR_CATEGORY, DASHBOARD_CATEGORY, @@ -533,6 +533,8 @@ const MyApp = ({ Component, pageProps }) => { RequiredAccess = Component.requiredAccess } + const { useFlag } = useFeatureFlags() + const isLegalInfoEnabled = useFlag(UI_LEGAL_INFO) const shouldDisplayCookieAgreement = router.pathname.match(/\/auth(\/.*)?/) // NOTE: We remember that the client has already been authorized, @@ -563,7 +565,7 @@ const MyApp = ({ Component, pageProps }) => { detectedMobileUserAgentInSSR={detectedMobileUserAgentInSSR} initialIsCollapsed={initialIsCollapsed} > - {shouldDisplayCookieAgreement && } + {shouldDisplayCookieAgreement && isLegalInfoEnabled && } From 9eef122c6a1b28ae8e63804bebc507f26999629e Mon Sep 17 00:00:00 2001 From: Mikhail Gorbunov Date: Thu, 28 May 2026 19:11:26 +0500 Subject: [PATCH 3/5] feat(condo): fix lint issues --- .../common/components/containers/BaseLayout/BaseLayout.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx b/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx index 2184d27c89d..b860b569de2 100644 --- a/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx +++ b/apps/condo/domains/common/components/containers/BaseLayout/BaseLayout.tsx @@ -4,9 +4,10 @@ import classnames from 'classnames' import Router from 'next/router' import React, { CSSProperties, FunctionComponent, ElementType } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' + import { useAIContext } from '@condo/domains/ai/components/AIContext' import { useLayoutContext } from '@condo/domains/common/components/LayoutContext' -import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { UI_HIDE_LAYOUT_HEADER, UI_HIDE_LAYOUT_SIDE_MENU } from '@condo/domains/common/constants/featureflags' import styles from './BaseLayout.module.css' From 9f93db4a8291df010c3ab5ea73da410c70e19366 Mon Sep 17 00:00:00 2001 From: Mikhail Gorbunov Date: Fri, 5 Jun 2026 19:14:39 +0500 Subject: [PATCH 4/5] fix(condo): fix insert -> select condition in executeAIFlow --- .../charts/payment/PaymentTotalChart.tsx | 6 +- .../BillingPageContent/BlockedB2BAppTab.tsx | 16 ++ .../domains/common/constants/featureflags.js | 2 + .../components/NoSubscriptionTooltip.tsx | 6 +- .../components/SubscriptionAccessGuard.tsx | 32 +++- .../components/SubscriptionBlockedContent.tsx | 12 +- .../components/SubscriptionDaysIndicator.tsx | 6 +- .../components/SubscriptionFeatureModal.tsx | 6 + .../SubscriptionFeatureProgress.tsx | 6 +- .../SubscriptionSettingsContent.tsx | 5 + .../components/UpgradePlanButton.tsx | 6 +- .../hooks/useLinkedCardsModal.tsx | 168 ++++++++++-------- .../hooks/usePaymentHistoryModal.tsx | 49 ++--- .../hooks/useSubscriptionPaymentModal.tsx | 10 +- apps/condo/lang/en/en.json | 2 + apps/condo/lang/es/es.json | 2 + apps/condo/lang/ru/ru.json | 2 + apps/condo/pages/_app.tsx | 11 +- apps/condo/pages/settings/index.tsx | 16 +- 19 files changed, 237 insertions(+), 126 deletions(-) diff --git a/apps/condo/domains/analytics/components/charts/payment/PaymentTotalChart.tsx b/apps/condo/domains/analytics/components/charts/payment/PaymentTotalChart.tsx index a19612c45e7..ea1cdd2900e 100644 --- a/apps/condo/domains/analytics/components/charts/payment/PaymentTotalChart.tsx +++ b/apps/condo/domains/analytics/components/charts/payment/PaymentTotalChart.tsx @@ -1,10 +1,12 @@ import { Row, Col } from 'antd' import React, { useMemo } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { Alert, Typography } from '@open-condo/ui' import { CustomChartView, CHART_CONTAINER_HEIGHT } from '@condo/domains/analytics/components/CustomChartView' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { SubscriptionGuardWithTooltip } from '@condo/domains/subscription/components' import { PaymentTotalDataMapper } from './dataMappers' @@ -13,6 +15,8 @@ import type { IPaymentChartCard } from './dataMappers' const PaymentTotalChart: IPaymentChartCard = ({ data }) => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const ChartTitle = intl.formatMessage({ id: 'pages.reports.paymentsTotal' }) const SumTitle = intl.formatMessage({ id: 'global.sum' }) const PaymentCountTitle = intl.formatMessage({ id: 'pages.reports.paymentCount' }) @@ -26,7 +30,7 @@ const PaymentTotalChart: IPaymentChartCard = ({ data }) => { {ChartTitle} - {data.length > 0 && + {!hidePaidFeatures && data.length > 0 && = ({ appId, shortDescription }) => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const GuardTitle = intl.formatMessage({ id: 'subscription.accessGuard.title' }) const FeatureGuardTitle = intl.formatMessage({ id: 'subscription.accessGuard.feature.title' }) const GoToPlansMessage = intl.formatMessage({ id: 'subscription.accessGuard.goToPlans' }) @@ -28,6 +32,8 @@ export const BlockedB2BAppTab: React.FC = ({ appId, short const AwaitingPaymentMessage = intl.formatMessage({ id: 'subscription.planCard.requestPending' }) const AwaitingPaymentTooltipMessage = intl.formatMessage({ id: 'subscription.planCard.requestPending.tooltip' }) const LearnMoreMessage = intl.formatMessage({ id: 'subscription.accessGuard.learnMore' }) + const UnavailableTitle = intl.formatMessage({ id: 'subscription.accessGuard.unavailable.title' }, { defaultMessage: 'Access denied' }) + const UnavailableDescription = intl.formatMessage({ id: 'subscription.accessGuard.unavailable.description' }, { defaultMessage: 'You do not have access to this service' }) const router = useRouter() @@ -85,6 +91,16 @@ export const BlockedB2BAppTab: React.FC = ({ appId, short ), [shortDescription, formattedFeaturePrice, forPlanLabel, FreeWithPlanNode]) + if (hidePaidFeatures) { + return ( + + ) + } + let primaryButton: React.ReactNode if (!hasFeaturePlan) { primaryButton = diff --git a/apps/condo/domains/common/constants/featureflags.js b/apps/condo/domains/common/constants/featureflags.js index fb2ab037aaa..81fd216dd1c 100644 --- a/apps/condo/domains/common/constants/featureflags.js +++ b/apps/condo/domains/common/constants/featureflags.js @@ -57,6 +57,7 @@ const UI_LEGAL_INFO = 'ui-legal-info' const UI_HIDE_USER_LINKS = 'ui-hide-user-links' const UI_HIDE_LAYOUT_HEADER = 'ui-hide-layout-header' const UI_HIDE_LAYOUT_SIDE_MENU = 'ui-hide-layout-sideMenu' +const UI_HIDE_PAID_FEATURES = 'ui-hide-paid-features' module.exports = { SMS_AFTER_TICKET_CREATION, @@ -118,4 +119,5 @@ module.exports = { UI_HIDE_USER_LINKS, UI_HIDE_LAYOUT_HEADER, UI_HIDE_LAYOUT_SIDE_MENU, + UI_HIDE_PAID_FEATURES, } diff --git a/apps/condo/domains/subscription/components/NoSubscriptionTooltip.tsx b/apps/condo/domains/subscription/components/NoSubscriptionTooltip.tsx index 17f63c609b3..985ea20fd8f 100644 --- a/apps/condo/domains/subscription/components/NoSubscriptionTooltip.tsx +++ b/apps/condo/domains/subscription/components/NoSubscriptionTooltip.tsx @@ -2,10 +2,12 @@ import { useGetAvailableServiceSubscriptionPlansQuery } from '@app/condo/gql' import { useRouter } from 'next/router' import React, { useCallback, useMemo, useState } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { useOrganization } from '@open-condo/next/organization' import { Button, Space, Tooltip, Typography } from '@open-condo/ui' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { SETTINGS_TAB_SUBSCRIPTION } from '@condo/domains/common/constants/settingsTabs' import { getRequiredFeature } from '@condo/domains/subscription/constants/routeFeatureMapping' import { @@ -28,6 +30,8 @@ export interface NoSubscriptionTooltipProps { export const NoSubscriptionTooltip: React.FC = ({ children, placement = 'right', feature: featureProp, path, skipTooltip, b2bAppId, tooltipButtonId }) => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const router = useRouter() const { organization } = useOrganization() const { activatedSubscriptions, registerSubscriptionContext, activateLoading } = useActivateSubscriptions() @@ -177,7 +181,7 @@ export const NoSubscriptionTooltip: React.FC = ({ ch ) - if (skipTooltip) { + if (hidePaidFeatures || skipTooltip) { return children } diff --git a/apps/condo/domains/subscription/components/SubscriptionAccessGuard.tsx b/apps/condo/domains/subscription/components/SubscriptionAccessGuard.tsx index 91a9add57bb..d4b09d1c918 100644 --- a/apps/condo/domains/subscription/components/SubscriptionAccessGuard.tsx +++ b/apps/condo/domains/subscription/components/SubscriptionAccessGuard.tsx @@ -4,6 +4,7 @@ import Head from 'next/head' import { useRouter } from 'next/router' import React, { useMemo, useCallback } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useAuth } from '@open-condo/next/auth' import { useIntl } from '@open-condo/next/intl' import { useOrganization } from '@open-condo/next/organization' @@ -11,6 +12,7 @@ import { Typography, Button, Tooltip } from '@open-condo/ui' import { PageHeader, PageWrapper } from '@condo/domains/common/components/containers/BaseLayout' import { Loader } from '@condo/domains/common/components/Loader' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { SETTINGS_TAB_SUBSCRIPTION } from '@condo/domains/common/constants/settingsTabs' import { SubscriptionBlockedContent } from './SubscriptionBlockedContent' @@ -72,6 +74,8 @@ const getPageTitle = (pathname: string, intl: any): string => { export const SubscriptionAccessGuard: React.FC = ({ children, skipGuard = false }) => { const router = useRouter() const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const GuardTitle = intl.formatMessage({ id: 'subscription.accessGuard.title' }) const GuardDescription = intl.formatMessage({ id: 'subscription.accessGuard.description' }) const GoToPlansMessage = intl.formatMessage({ id: 'subscription.accessGuard.goToPlans' }) @@ -80,6 +84,8 @@ export const SubscriptionAccessGuard: React.FC = ( const FeaturePayButton = intl.formatMessage({ id: 'subscription.accessGuard.feature.payButton' }) const AwaitingPaymentMessage = intl.formatMessage({ id: 'subscription.planCard.requestPending' }) const AwaitingPaymentTooltipMessage = intl.formatMessage({ id: 'subscription.planCard.requestPending.tooltip' }) + const UnavailableTitle = intl.formatMessage({ id: 'subscription.accessGuard.unavailable.title' }, { defaultMessage: 'Access denied' }) + const UnavailableDescription = intl.formatMessage({ id: 'subscription.accessGuard.unavailable.description' }, { defaultMessage: 'You do not have access to this service' }) const { isFeatureAvailable, isB2BAppEnabled, hasSubscription, loading, hasSubscriptionsFeature } = useOrganizationSubscription() const { organization, isLoading: orgIsLoading } = useOrganization() const { isLoading: authIsLoading } = useAuth() @@ -200,6 +206,24 @@ export const SubscriptionAccessGuard: React.FC = ( : null if (isBlocked) { + if (hidePaidFeatures && !hasSubscription) { + return ( + <> + + {pageTitle} + + + {pageTitle}} /> + + + + ) + } + let primaryButton: React.ReactNode if (!hasFeaturePlan) { primaryButton = @@ -229,8 +253,8 @@ export const SubscriptionAccessGuard: React.FC = ( return ( <> - {PaymentModal} - + {!hidePaidFeatures && PaymentModal} + {!hidePaidFeatures && } {pageTitle} @@ -251,8 +275,8 @@ export const SubscriptionAccessGuard: React.FC = ( return ( <> - - + {!hidePaidFeatures && } + {!hidePaidFeatures && } {children} ) diff --git a/apps/condo/domains/subscription/components/SubscriptionBlockedContent.tsx b/apps/condo/domains/subscription/components/SubscriptionBlockedContent.tsx index a378fa62d72..6d1d3a9f6e9 100644 --- a/apps/condo/domains/subscription/components/SubscriptionBlockedContent.tsx +++ b/apps/condo/domains/subscription/components/SubscriptionBlockedContent.tsx @@ -9,7 +9,7 @@ import styles from './SubscriptionBlockedContent.module.css' type SubscriptionBlockedContentProps = { title: string description: React.ReactNode - primaryButton: React.ReactNode + primaryButton?: React.ReactNode secondaryButton?: React.ReactNode } @@ -27,10 +27,12 @@ export const SubscriptionBlockedContent: React.FC{title} {description} - - {primaryButton} - {secondaryButton} - + {(primaryButton || secondaryButton) && ( + + {primaryButton} + {secondaryButton} + + )}
diff --git a/apps/condo/domains/subscription/components/SubscriptionDaysIndicator.tsx b/apps/condo/domains/subscription/components/SubscriptionDaysIndicator.tsx index 8a28f94a75b..27f1762074f 100644 --- a/apps/condo/domains/subscription/components/SubscriptionDaysIndicator.tsx +++ b/apps/condo/domains/subscription/components/SubscriptionDaysIndicator.tsx @@ -2,10 +2,12 @@ import { Progress, Tooltip } from 'antd' import dayjs from 'dayjs' import React, { useMemo } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { Typography } from '@open-condo/ui' import { colors } from '@open-condo/ui/colors' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { useOrganizationSubscription } from '@condo/domains/subscription/hooks' const BRAND_GRADIENT = { @@ -18,6 +20,8 @@ const MAX_VALUE_IN_PAID_INDICATOR = 30 export const SubscriptionDaysIndicator: React.FC = () => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const { subscriptionContext, daysRemaining, daysRemainingWithoutBuffer, isInBufferPeriod } = useOrganizationSubscription() const isTrial = subscriptionContext?.isTrial @@ -107,7 +111,7 @@ export const SubscriptionDaysIndicator: React.FC = () => { ) }, [isTrial, isInBufferPeriod, intl, planName, endDate, daysRemaining, daysRemainingWithoutBuffer]) - if (!shouldShow) return null + if (hidePaidFeatures || !shouldShow) return null return ( diff --git a/apps/condo/domains/subscription/components/SubscriptionFeatureModal.tsx b/apps/condo/domains/subscription/components/SubscriptionFeatureModal.tsx index 04afd2c1ea0..ee3c2b5cc3a 100644 --- a/apps/condo/domains/subscription/components/SubscriptionFeatureModal.tsx +++ b/apps/condo/domains/subscription/components/SubscriptionFeatureModal.tsx @@ -2,9 +2,11 @@ import { AvailableSubscriptionPlan } from '@app/condo/schema' import { useRouter } from 'next/router' import React, { useCallback, useMemo, useState } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { Banner, Button, Modal, Space, Typography } from '@open-condo/ui' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { SETTINGS_TAB_SUBSCRIPTION } from '@condo/domains/common/constants/settingsTabs' import { useActivateSubscriptions, useTrialSubscriptions } from '@condo/domains/subscription/hooks' @@ -43,6 +45,8 @@ export const SubscriptionFeatureModal: React.FC = subscriptionModalConfig, }) => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const TryTrialButtonText = intl.formatMessage({ id: 'subscription.featureProgress.modal.tryTrialButton' }) const router = useRouter() const { registerSubscriptionContext, activateLoading } = useActivateSubscriptions() @@ -124,6 +128,8 @@ export const SubscriptionFeatureModal: React.FC = ) }, [hasActivatedAnyTrial, handleViewPlans, handleActivateTrial, viewPlansButtonText, TryTrialButtonText, isLoading]) + if (hidePaidFeatures) return null + return ( { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const TooltipTitle = intl.formatMessage({ id: 'subscription.featureProgress.tooltip' }) const { organization } = useOrganization() const { isFeatureAvailable, isB2BAppEnabled } = useOrganizationSubscription() @@ -159,7 +163,7 @@ export const SubscriptionFeatureProgress: React.FC = () => { openModal() } - if (!bestPlan || !bestPlan?.prices?.[0] || !hasSubscriptionModalConfig) { + if (hidePaidFeatures || !bestPlan || !bestPlan?.prices?.[0] || !hasSubscriptionModalConfig) { return null } diff --git a/apps/condo/domains/subscription/components/SubscriptionSettingsContent/SubscriptionSettingsContent.tsx b/apps/condo/domains/subscription/components/SubscriptionSettingsContent/SubscriptionSettingsContent.tsx index faac38db98a..68a9f4420b2 100644 --- a/apps/condo/domains/subscription/components/SubscriptionSettingsContent/SubscriptionSettingsContent.tsx +++ b/apps/condo/domains/subscription/components/SubscriptionSettingsContent/SubscriptionSettingsContent.tsx @@ -1,11 +1,13 @@ import { useGetAvailableServiceSubscriptionPlansQuery, GetAvailableServiceSubscriptionPlansQueryResult, useGetPublicB2BAppsByIdsQuery } from '@app/condo/gql' import React, { useState, useMemo, useCallback } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { useOrganization } from '@open-condo/next/organization' import { Space, Radio } from '@open-condo/ui' import { Loader } from '@condo/domains/common/components/Loader' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { useActivateSubscriptions, useTrialSubscriptions } from '@condo/domains/subscription/hooks' import { PromoBanner } from './PromoBanner/PromoBanner' @@ -20,6 +22,8 @@ const PLAN_CARD_EMOJIS = ['🏠', '🚀', '👑'] export const SubscriptionSettingsContent: React.FC = () => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const { organization } = useOrganization() const YearlyLabel = intl.formatMessage({ id: 'subscription.period.yearly' }) @@ -83,6 +87,7 @@ export const SubscriptionSettingsContent: React.FC = () => { }, [refetchActivatedSubscriptions]) const isLoading = plansLoading || trialActivationLoading + if (hidePaidFeatures) return null if (isLoading) return return ( diff --git a/apps/condo/domains/subscription/components/UpgradePlanButton.tsx b/apps/condo/domains/subscription/components/UpgradePlanButton.tsx index 73e0231791f..25c8c5e4249 100644 --- a/apps/condo/domains/subscription/components/UpgradePlanButton.tsx +++ b/apps/condo/domains/subscription/components/UpgradePlanButton.tsx @@ -8,7 +8,7 @@ import { useIntl } from '@open-condo/next/intl' import { useOrganization } from '@open-condo/next/organization' import { Button } from '@open-condo/ui' -import { SUBSCRIPTIONS } from '@condo/domains/common/constants/featureflags' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { useOrganizationSubscription } from '@condo/domains/subscription/hooks' import styles from './UpgradePlanButton.module.css' @@ -20,6 +20,8 @@ export const UpgradePlanButton: React.FC = () => { const router = useRouter() const { organization } = useOrganization() const { subscriptionContext, hasSubscriptionsFeature } = useOrganizationSubscription() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const { data: plansData, loading: plansLoading } = useGetAvailableServiceSubscriptionPlansQuery({ variables: { @@ -82,7 +84,7 @@ export const UpgradePlanButton: React.FC = () => { router.push('/settings?tab=subscription') } - if (!buttonText || !hasSubscriptionsFeature) return null + if (hidePaidFeatures || !buttonText || !hasSubscriptionsFeature) return null /*TODO DOMA-12785 move to ui kit*/ return ( , - ]} - > - - {UnbindCardMessage} - - - - ), [isModalOpen, closeModal, LinkedCardsTitle, paymentMethods, imageErrors, getCardTypeTranslation, getCardLabel, handleUnbindClick, canManageSubscriptions, isConfirmModalOpen, closeConfirmModal, UnbindCardTitle, isUnbinding, handleUnbindConfirm, UnbindButtonLabel, UnbindCardMessage]) + const LinkedCardsModal = useMemo(() => { + if (hidePaidFeatures) return null + + return ( + <> + + + {paymentMethods.map((paymentMethod) => { + const cardLabel = getCardLabel(paymentMethod.bindingId) + return ( + + + + {paymentMethod.cardIssuerName handleImageError(paymentMethod.bindingId)} + /> +
+ + {getCardTypeTranslation(paymentMethod.paymentSystem)} ∙ {paymentMethod.cardMask?.slice(-4)} + {cardLabel && ( + + {' '}{cardLabel} + + )} + +
+ + + , + ]} + > + + {UnbindCardMessage} + +
+ + ) + }, [hidePaidFeatures, isModalOpen, closeModal, LinkedCardsTitle, paymentMethods, imageErrors, getCardTypeTranslation, getCardLabel, handleUnbindClick, canManageSubscriptions, isConfirmModalOpen, closeConfirmModal, UnbindCardTitle, isUnbinding, handleUnbindConfirm, UnbindButtonLabel, UnbindCardMessage]) return { LinkedCardsModal, openModal, - hasPaymentMethod: paymentMethods.length > 0, + hasPaymentMethod: !hidePaidFeatures && paymentMethods.length > 0, } } diff --git a/apps/condo/domains/subscription/hooks/usePaymentHistoryModal.tsx b/apps/condo/domains/subscription/hooks/usePaymentHistoryModal.tsx index 2e83631204f..64f51d5e6c4 100644 --- a/apps/condo/domains/subscription/hooks/usePaymentHistoryModal.tsx +++ b/apps/condo/domains/subscription/hooks/usePaymentHistoryModal.tsx @@ -4,6 +4,7 @@ import getConfig from 'next/config' import { useRouter } from 'next/router' import { useMemo, useCallback, useState } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { useOrganization } from '@open-condo/next/organization' import { Modal, Typography } from '@open-condo/ui' @@ -12,6 +13,7 @@ import { Table } from '@condo/domains/common/components/Table/Index' import { getDateRender, } from '@condo/domains/common/components/Table/Renders' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' import { getPageIndexFromOffset, parseQuery } from '@condo/domains/common/utils/tables.utils' const { publicRuntimeConfig } = getConfig() @@ -23,6 +25,8 @@ type PaymentHistoryRecord = GetOrganizationPaymentHistoryQuery['paymentHistory'] export const usePaymentHistoryModal = () => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const router = useRouter() const { organization } = useOrganization() const [isModalOpen, setIsModalOpen] = useState(false) @@ -73,8 +77,9 @@ export const usePaymentHistoryModal = () => { const hasPaymentHistory = totalCount > 0 const openModal = useCallback(() => { + if (hidePaidFeatures) return setIsModalOpen(true) - }, []) + }, [hidePaidFeatures]) const closeModal = useCallback(() => { setIsModalOpen(false) @@ -142,28 +147,32 @@ export const usePaymentHistoryModal = () => { const getRowId = useCallback((row: PaymentHistoryRecord) => row.id, []) - const PaymentHistoryModal = useMemo(() => ( - - - - ), [isModalOpen, closeModal, PaymentHistoryTitle, loading, paymentHistory, columns, totalCount, getRowId]) + const PaymentHistoryModal = useMemo(() => { + if (hidePaidFeatures) return null + + return ( + +
+ + ) + }, [hidePaidFeatures, isModalOpen, closeModal, PaymentHistoryTitle, loading, paymentHistory, columns, totalCount, getRowId]) return { PaymentHistoryModal, openModal, - hasPaymentHistory, + hasPaymentHistory: !hidePaidFeatures && hasPaymentHistory, } } diff --git a/apps/condo/domains/subscription/hooks/useSubscriptionPaymentModal.tsx b/apps/condo/domains/subscription/hooks/useSubscriptionPaymentModal.tsx index 1fca15501a0..da0bf89bb24 100644 --- a/apps/condo/domains/subscription/hooks/useSubscriptionPaymentModal.tsx +++ b/apps/condo/domains/subscription/hooks/useSubscriptionPaymentModal.tsx @@ -1,10 +1,12 @@ import { Row, Col, RowProps } from 'antd' import React, { useCallback, useMemo, useState } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { Button, Card, Checkbox, Modal, Typography } from '@open-condo/ui' import { useLayoutContext } from '@condo/domains/common/components/LayoutContext' +import { UI_HIDE_PAID_FEATURES } from '@condo/domains/common/constants/featureflags' export type PaymentType = 'card' | 'userHelpRequest' @@ -33,6 +35,8 @@ export const useSubscriptionPaymentModal = ({ activateLoading, }: UseSubscriptionPaymentModalProps): UseSubscriptionPaymentModalReturn => { const intl = useIntl() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const ModalTitleMessage = intl.formatMessage({ id: 'subscription.paymentModal.title' }) const CardOnlineMessage = intl.formatMessage({ id: 'subscription.paymentModal.cardOnline' }) const InvoiceMessage = intl.formatMessage({ id: 'subscription.paymentModal.invoice' }) @@ -46,9 +50,10 @@ export const useSubscriptionPaymentModal = ({ const [selectedMethod, setSelectedMethod] = useState('card') const openModal = useCallback(() => { + if (hidePaidFeatures) return setSelectedMethod('card') setOpen(true) - }, []) + }, [hidePaidFeatures]) const closeModal = useCallback(() => { setOpen(false) @@ -74,6 +79,8 @@ export const useSubscriptionPaymentModal = ({ }, [registerSubscriptionContext, closeModal]) const PaymentModal = useMemo(() => { + if (hidePaidFeatures) return null + const isCardSelected = selectedMethod === 'card' const isUserHelpRequestSelected = selectedMethod === 'userHelpRequest' @@ -225,6 +232,7 @@ export const useSubscriptionPaymentModal = ({ CardSaveNoticeMessage, ProceedToPaymentMessage, IssueInvoiceMessage, + hidePaidFeatures, breakpoints.TABLET_LARGE, open, selectedMethod, diff --git a/apps/condo/lang/en/en.json b/apps/condo/lang/en/en.json index 932ac70f329..109ade5e4f2 100644 --- a/apps/condo/lang/en/en.json +++ b/apps/condo/lang/en/en.json @@ -3189,6 +3189,8 @@ "subscription.accessGuard.goToPlans": "Learn more about plans", "subscription.accessGuard.learnMore": "Learn more about this section", "subscription.accessGuard.defaultPageTitle": "Section", + "subscription.accessGuard.unavailable.title": "Access denied", + "subscription.accessGuard.unavailable.description": "You do not have access to this service", "subscription.accessGuard.feature.title": "Upgrade your plan or pay for the feature separately", "subscription.accessGuard.feature.payButton": "Pay", "subscription.accessGuard.feature.freeWithPlan": "free with \"{planName}\" plan", diff --git a/apps/condo/lang/es/es.json b/apps/condo/lang/es/es.json index 6197397dcd0..d149aa2da25 100644 --- a/apps/condo/lang/es/es.json +++ b/apps/condo/lang/es/es.json @@ -3189,6 +3189,8 @@ "subscription.accessGuard.goToPlans": "Más información sobre los planes", "subscription.accessGuard.learnMore": "Aprender más sobre esta sección", "subscription.accessGuard.defaultPageTitle": "Sección", + "subscription.accessGuard.unavailable.title": "Acceso denegado", + "subscription.accessGuard.unavailable.description": "No tienes acceso a este servicio", "subscription.accessGuard.feature.title": "Mejore su plan o pague la función por separado", "subscription.accessGuard.feature.payButton": "Pagar", "subscription.accessGuard.feature.freeWithPlan": "gratis con el plan «{planName}»", diff --git a/apps/condo/lang/ru/ru.json b/apps/condo/lang/ru/ru.json index bf02a5af711..a22f383b3d7 100644 --- a/apps/condo/lang/ru/ru.json +++ b/apps/condo/lang/ru/ru.json @@ -3189,6 +3189,8 @@ "subscription.accessGuard.goToPlans": "Подробнее о тарифах", "subscription.accessGuard.learnMore": "Узнать больше о разделе", "subscription.accessGuard.defaultPageTitle": "Раздел", + "subscription.accessGuard.unavailable.title": "Сервис недоступен", + "subscription.accessGuard.unavailable.description": "У вас нет доступа к этому сервису", "subscription.accessGuard.feature.title": "Прокачайте свой тариф или оплатите функционал отдельно", "subscription.accessGuard.feature.payButton": "Оплатить", "subscription.accessGuard.feature.freeWithPlan": "бесплатно с тарифом «{planName}»", diff --git a/apps/condo/pages/_app.tsx b/apps/condo/pages/_app.tsx index 6094f929092..c7fa7490864 100644 --- a/apps/condo/pages/_app.tsx +++ b/apps/condo/pages/_app.tsx @@ -54,7 +54,7 @@ import { SurveysQueue } from '@condo/domains/common/components/surveys/SurveyQue import { TasksContextProvider } from '@condo/domains/common/components/tasks/TasksContextProvider' import UseDeskWidget from '@condo/domains/common/components/UseDeskWidget' import { COOKIE_MAX_AGE_IN_SEC } from '@condo/domains/common/constants/cookies' -import { SERVICE_PROVIDER_PROFILE, UI_LEGAL_INFO } from '@condo/domains/common/constants/featureflags' +import { SERVICE_PROVIDER_PROFILE, UI_HIDE_PAID_FEATURES, UI_LEGAL_INFO } from '@condo/domains/common/constants/featureflags' import { TOUR_CATEGORY, DASHBOARD_CATEGORY, @@ -168,6 +168,7 @@ const ANT_DEFAULT_LOCALE = enUS const MenuItems: React.FC = () => { const { updateContext, useFlag } = useFeatureFlags() const isSPPOrg = useFlag(SERVICE_PROVIDER_PROFILE) + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const { persistor } = useCachePersistor() const { isAuthenticated, isLoading } = useAuth() @@ -378,13 +379,13 @@ const MenuItems: React.FC = () => { return (
- {hasSubscription && hasSubscriptionsFeature && } + {!hidePaidFeatures && hasSubscription && hasSubscriptionsFeature && } {menuCategoriesData.map((category) => ( {category.items.map((item) => { const isSubscriptionPage = item.path === 'settings' const isDisabled = isSubscriptionPage ? !employee : disabled - const featureTooltip = ({ element, placement }) => ( + const featureTooltip = hidePaidFeatures ? null : ({ element, placement }) => ( ) @@ -415,10 +416,12 @@ const MenuItems: React.FC = () => { let tooltipDecorator = null if (disabled) { tooltipDecorator = wrapElementIntoNoOrganizationToolTip - } else if (!isAppAvailable) { + } else if (!hidePaidFeatures && !isAppAvailable) { tooltipDecorator = miniappTooltip } + if (hidePaidFeatures && !isAppAvailable) return null + return { const canManageMarketSettingRoles = useMemo(() => userOrganization?.role?.canManageMarketSetting || false, [userOrganization]) const { hasSubscription, hasAvailablePlans, loading: subscriptionsLoading, subscriptionContext, hasSubscriptionsFeature } = useOrganizationSubscription() + const { useFlag } = useFeatureFlags() + const hidePaidFeatures = useFlag(UI_HIDE_PAID_FEATURES) const isSubscriptionsEnabled = !subscriptionsLoading && hasAvailablePlans && hasSubscriptionsFeature const { objs: [acquiringIntegrationContext], loading: acquiringIntegrationContextLoading } = AcquiringIntegrationContext.useObjects({ @@ -81,7 +83,7 @@ const SettingsPage: PageComponentType = () => { const loading = subscriptionsLoading || acquiringIntegrationContextLoading const availableTabs = useMemo(() => { - if (!hasSubscription && isSubscriptionsEnabled) return [SETTINGS_TAB_SUBSCRIPTION] + if (!hidePaidFeatures && !hasSubscription && isSubscriptionsEnabled) return [SETTINGS_TAB_SUBSCRIPTION] const availableTabs = [...ALWAYS_AVAILABLE_TABS] if (canManageEmployeeRoles && isManagingCompany) availableTabs.push(SETTINGS_TAB_EMPLOYEE_ROLES) @@ -90,10 +92,10 @@ const SettingsPage: PageComponentType = () => { if (isManagingCompany) availableTabs.push(SETTINGS_TAB_CONTROL_ROOM) if (canManageMobileFeatureConfigsRoles) availableTabs.push(SETTINGS_TAB_MOBILE_FEATURE_CONFIG) if (canManageMarketSettingRoles && Boolean(acquiringIntegrationContext) && !acquiringIntegrationContextLoading) availableTabs.push(SETTINGS_TAB_MARKETPLACE) - if (isSubscriptionsEnabled) availableTabs.push(SETTINGS_TAB_SUBSCRIPTION) + if (!hidePaidFeatures && isSubscriptionsEnabled) availableTabs.push(SETTINGS_TAB_SUBSCRIPTION) return availableTabs - }, [hasSubscription, isSubscriptionsEnabled, canManageEmployeeRoles, isManagingCompany, canManageContactRoles, canManageMobileFeatureConfigsRoles, canManageMarketSettingRoles, acquiringIntegrationContext, acquiringIntegrationContextLoading]) + }, [hidePaidFeatures, hasSubscription, isSubscriptionsEnabled, canManageEmployeeRoles, isManagingCompany, canManageContactRoles, canManageMobileFeatureConfigsRoles, canManageMarketSettingRoles, acquiringIntegrationContext, acquiringIntegrationContextLoading]) const settingsTabs: TabItem[] = useMemo(() => { const subscriptionTab = { @@ -102,7 +104,7 @@ const SettingsPage: PageComponentType = () => { children: , } - if (!hasSubscription && isSubscriptionsEnabled) return [subscriptionTab] + if (!hidePaidFeatures && !hasSubscription && isSubscriptionsEnabled) return [subscriptionTab] return [ canManageEmployeeRoles && isManagingCompany && { @@ -135,9 +137,9 @@ const SettingsPage: PageComponentType = () => { label: MarketSettingTitle, children: , }, - isSubscriptionsEnabled && subscriptionTab, + !hidePaidFeatures && isSubscriptionsEnabled && subscriptionTab, ].filter(Boolean) - }, [SubscriptionsTitle, hasSubscription, isSubscriptionsEnabled, canManageEmployeeRoles, isManagingCompany, EmployeeRolesTitle, DetailsTitle, canManageContactRoles, RolesTitle, ControlRoomTitle, canManageMobileFeatureConfigsRoles, MobileFeatureConfigTitle, canManageMarketSettingRoles, acquiringIntegrationContext, acquiringIntegrationContextLoading, MarketSettingTitle]) + }, [SubscriptionsTitle, hidePaidFeatures, hasSubscription, isSubscriptionsEnabled, canManageEmployeeRoles, isManagingCompany, EmployeeRolesTitle, DetailsTitle, canManageContactRoles, RolesTitle, ControlRoomTitle, canManageMobileFeatureConfigsRoles, MobileFeatureConfigTitle, canManageMarketSettingRoles, acquiringIntegrationContext, acquiringIntegrationContextLoading, MarketSettingTitle]) const titleContent = useMemo(() => ( {PageTitle} From 6ec7eb029146b309e88e0a434adef09571049529 Mon Sep 17 00:00:00 2001 From: Mikhail Gorbunov Date: Sun, 7 Jun 2026 13:33:21 +0500 Subject: [PATCH 5/5] fix(condo): fix lint issues --- apps/condo/domains/common/hooks/useGlobalHints.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/condo/domains/common/hooks/useGlobalHints.tsx b/apps/condo/domains/common/hooks/useGlobalHints.tsx index 7cbf9f53873..659c37d7a7c 100644 --- a/apps/condo/domains/common/hooks/useGlobalHints.tsx +++ b/apps/condo/domains/common/hooks/useGlobalHints.tsx @@ -149,7 +149,7 @@ export const useGlobalHints = () => {
- ), [CanDisableHintsMessage, InProfileMessage, MoreMessage, handleBannerClick, hints]) + ), [CanDisableHintsMessage, InProfileMessage, isUserLinksHidden, MoreMessage, handleBannerClick, hints]) return { GlobalHints: renderHints,