From 89c7f7119986afb6f248b7ef104310a2d3862b1a Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 15 Aug 2025 14:16:35 +0800 Subject: [PATCH] feat: add Footer component and integrate it into Pricing layout; refactor Header styles and update Plans component structure --- web/app/components/billing/pricing/footer.tsx | 32 +++++++++++ web/app/components/billing/pricing/header.tsx | 2 +- web/app/components/billing/pricing/index.tsx | 39 ++++---------- .../{plan-item => cloud-plan-item}/button.tsx | 0 .../{plan-item => cloud-plan-item}/index.tsx | 10 ++-- .../list/index.tsx | 0 .../list/item/index.tsx | 0 .../list/item/tooltip.tsx | 0 .../billing/pricing/plans/index.tsx | 45 ++++++++-------- .../self-hosted-plan-item/index.tsx} | 16 +++--- .../billing/pricing/select-plan-range.tsx | 54 ------------------- 11 files changed, 80 insertions(+), 118 deletions(-) create mode 100644 web/app/components/billing/pricing/footer.tsx rename web/app/components/billing/pricing/plans/{plan-item => cloud-plan-item}/button.tsx (100%) rename web/app/components/billing/pricing/plans/{plan-item => cloud-plan-item}/index.tsx (94%) rename web/app/components/billing/pricing/plans/{plan-item => cloud-plan-item}/list/index.tsx (100%) rename web/app/components/billing/pricing/plans/{plan-item => cloud-plan-item}/list/item/index.tsx (100%) rename web/app/components/billing/pricing/plans/{plan-item => cloud-plan-item}/list/item/tooltip.tsx (100%) rename web/app/components/billing/pricing/{self-hosted-plan-item.tsx => plans/self-hosted-plan-item/index.tsx} (92%) delete mode 100644 web/app/components/billing/pricing/select-plan-range.tsx diff --git a/web/app/components/billing/pricing/footer.tsx b/web/app/components/billing/pricing/footer.tsx new file mode 100644 index 0000000000..0fb9262394 --- /dev/null +++ b/web/app/components/billing/pricing/footer.tsx @@ -0,0 +1,32 @@ +import React from 'react' +import Link from 'next/link' +import { useTranslation } from 'react-i18next' +import { RiArrowRightUpLine } from '@remixicon/react' + +type FooterProps = { + pricingPageURL: string +} + +const Footer = ({ + pricingPageURL, +}: FooterProps) => { + const { t } = useTranslation() + + return ( +
+
+ + + {t('billing.plansCommon.comparePlanAndFeatures')} + + + +
+
+ ) +} + +export default React.memo(Footer) diff --git a/web/app/components/billing/pricing/header.tsx b/web/app/components/billing/pricing/header.tsx index 60f6d3d248..149bbe467f 100644 --- a/web/app/components/billing/pricing/header.tsx +++ b/web/app/components/billing/pricing/header.tsx @@ -20,7 +20,7 @@ const Header = ({
- + {t('billing.plansCommon.title.plans')} diff --git a/web/app/components/billing/pricing/index.tsx b/web/app/components/billing/pricing/index.tsx index e65de95b8e..112eeda788 100644 --- a/web/app/components/billing/pricing/index.tsx +++ b/web/app/components/billing/pricing/index.tsx @@ -4,21 +4,13 @@ import React, { useState } from 'react' import { createPortal } from 'react-dom' import Header from './header' import PlanSwitcher from './plan-switcher' +import Plans from './plans' +import Footer from './footer' import { PlanRange } from './plan-switcher/plan-range-switcher' import { useKeyPress } from 'ahooks' import { useProviderContext } from '@/context/provider-context' import { useAppContext } from '@/context/app-context' -import Plans from './plans' -// import { useTranslation } from 'react-i18next' -// import { RiArrowRightUpLine, RiCloseLine, RiCloudFill, RiTerminalBoxFill } from '@remixicon/react' -// import Link from 'next/link' -// import { Plan, SelfHostedPlan } from '../type' -// import TabSlider from '../../base/tab-slider' -// import PlanItem from './plan-item' -// import SelfHostedPlanItem from './self-hosted-plan-item' -// import GridMask from '@/app/components/base/grid-mask' -// import classNames from '@/utils/classnames' -// import { useGetPricingPageLanguage } from '@/context/i18n' +import { useGetPricingPageLanguage } from '@/context/i18n' export type Category = 'cloud' | 'self' @@ -29,27 +21,25 @@ type PricingProps = { const Pricing: FC = ({ onCancel, }) => { - // const { t } = useTranslation() const { plan } = useProviderContext() const { isCurrentWorkspaceManager } = useAppContext() - const canPay = isCurrentWorkspaceManager const [planRange, setPlanRange] = React.useState(PlanRange.monthly) - const [currentCategory, setCurrentCategory] = useState('cloud') + const canPay = isCurrentWorkspaceManager useKeyPress(['esc'], onCancel) - // const pricingPageLanguage = useGetPricingPageLanguage() - // const pricingPageURL = pricingPageLanguage - // ? `https://dify.ai/${pricingPageLanguage}/pricing#plans-and-features` - // : 'https://dify.ai/pricing#plans-and-features' + const pricingPageLanguage = useGetPricingPageLanguage() + const pricingPageURL = pricingPageLanguage + ? `https://dify.ai/${pricingPageLanguage}/pricing#plans-and-features` + : 'https://dify.ai/pricing#plans-and-features' return createPortal(
e.stopPropagation()} > -
+
= ({ planRange={planRange} canPay={canPay} /> - {/* -
-
- {t('billing.plansCommon.comparePlanAndFeatures')} - -
-
-
*/} +
-
, +
, document.body, ) } diff --git a/web/app/components/billing/pricing/plans/plan-item/button.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/button.tsx similarity index 100% rename from web/app/components/billing/pricing/plans/plan-item/button.tsx rename to web/app/components/billing/pricing/plans/cloud-plan-item/button.tsx diff --git a/web/app/components/billing/pricing/plans/plan-item/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx similarity index 94% rename from web/app/components/billing/pricing/plans/plan-item/index.tsx rename to web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx index 874038d234..9e36cfd233 100644 --- a/web/app/components/billing/pricing/plans/plan-item/index.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx @@ -6,7 +6,7 @@ import type { BasicPlan } from '../../../type' import { Plan } from '../../../type' import { ALL_PLANS } from '../../../config' import Toast from '../../../../base/toast' -import { PlanRange } from '../../select-plan-range' +import { PlanRange } from '../../plan-switcher/plan-range-switcher' import { useAppContext } from '@/context/app-context' import { fetchSubscriptionUrls } from '@/service/billing' import List from './list' @@ -18,14 +18,14 @@ const ICON_MAP = { [Plan.team]:
, } -type PlanItemProps = { +type CloudPlanItemProps = { currentPlan: BasicPlan plan: BasicPlan planRange: PlanRange canPay: boolean } -const PlanItem: FC = ({ +const CloudPlanItem: FC = ({ plan, currentPlan, planRange, @@ -82,7 +82,7 @@ const PlanItem: FC = ({ } } return ( -
+
{ICON_MAP[plan]} @@ -129,4 +129,4 @@ const PlanItem: FC = ({
) } -export default React.memo(PlanItem) +export default React.memo(CloudPlanItem) diff --git a/web/app/components/billing/pricing/plans/plan-item/list/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.tsx similarity index 100% rename from web/app/components/billing/pricing/plans/plan-item/list/index.tsx rename to web/app/components/billing/pricing/plans/cloud-plan-item/list/index.tsx diff --git a/web/app/components/billing/pricing/plans/plan-item/list/item/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/index.tsx similarity index 100% rename from web/app/components/billing/pricing/plans/plan-item/list/item/index.tsx rename to web/app/components/billing/pricing/plans/cloud-plan-item/list/item/index.tsx diff --git a/web/app/components/billing/pricing/plans/plan-item/list/item/tooltip.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/tooltip.tsx similarity index 100% rename from web/app/components/billing/pricing/plans/plan-item/list/item/tooltip.tsx rename to web/app/components/billing/pricing/plans/cloud-plan-item/list/item/tooltip.tsx diff --git a/web/app/components/billing/pricing/plans/index.tsx b/web/app/components/billing/pricing/plans/index.tsx index 8360542ffb..5142763531 100644 --- a/web/app/components/billing/pricing/plans/index.tsx +++ b/web/app/components/billing/pricing/plans/index.tsx @@ -1,7 +1,8 @@ import Divider from '@/app/components/base/divider' -import { type BasicPlan, Plan, type UsagePlanInfo } from '../../type' -import PlanItem from './plan-item' +import { type BasicPlan, Plan, SelfHostedPlan, type UsagePlanInfo } from '../../type' +import CloudPlanItem from './cloud-plan-item' import type { PlanRange } from '../plan-switcher/plan-range-switcher' +import SelfHostedPlanItem from './self-hosted-plan-item' type PlansProps = { plan: { @@ -26,21 +27,21 @@ const Plans = ({ { currentPlan === 'cloud' && ( <> - - - - // - // - // - // + currentPlan === 'self' && <> + + + + }
diff --git a/web/app/components/billing/pricing/self-hosted-plan-item.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx similarity index 92% rename from web/app/components/billing/pricing/self-hosted-plan-item.tsx rename to web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx index b937d5fc53..62b9e015e0 100644 --- a/web/app/components/billing/pricing/self-hosted-plan-item.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx @@ -3,12 +3,12 @@ import type { FC, ReactNode } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import { RiArrowRightUpLine, RiBrain2Line, RiCheckLine, RiQuestionLine } from '@remixicon/react' -import { SelfHostedPlan } from '../type' -import { contactSalesUrl, getStartedWithCommunityUrl, getWithPremiumUrl } from '../config' -import Toast from '../../base/toast' -import Tooltip from '../../base/tooltip' -import { Asterisk, AwsMarketplace, Azure, Buildings, Diamond, GoogleCloud } from '../../base/icons/src/public/billing' -import type { PlanRange } from './select-plan-range' +import { SelfHostedPlan } from '../../../type' +import { contactSalesUrl, getStartedWithCommunityUrl, getWithPremiumUrl } from '../../../config' +import Toast from '../../../../base/toast' +import Tooltip from '../../../../base/tooltip' +import { Asterisk, AwsMarketplace, Azure, Buildings, Diamond, GoogleCloud } from '../../../../base/icons/src/public/billing' +import type { PlanRange } from '../../plan-switcher/plan-range-switcher' import cn from '@/utils/classnames' import { useAppContext } from '@/context/app-context' @@ -113,8 +113,8 @@ const SelfHostedPlanItem: FC = ({
- {isEnterprisePlan &&
} - {isEnterprisePlan &&
} + {isEnterprisePlan &&
} + {isEnterprisePlan &&
}
diff --git a/web/app/components/billing/pricing/select-plan-range.tsx b/web/app/components/billing/pricing/select-plan-range.tsx deleted file mode 100644 index e7b06cb2ca..0000000000 --- a/web/app/components/billing/pricing/select-plan-range.tsx +++ /dev/null @@ -1,54 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import Switch from '../../base/switch' -export enum PlanRange { - monthly = 'monthly', - yearly = 'yearly', -} - -type Props = { - value: PlanRange - onChange: (value: PlanRange) => void -} - -const ArrowIcon = ( - - - - - - - - - - - - - - -) - -const SelectPlanRange: FC = ({ - value, - onChange, -}) => { - const { t } = useTranslation() - - return ( -
-
{t('billing.plansCommon.yearlyTip')}
-
- {t('billing.plansCommon.annualBilling')} - { - onChange(v ? PlanRange.yearly : PlanRange.monthly) - }} /> -
-
- {ArrowIcon} -
-
- ) -} -export default React.memo(SelectPlanRange)