From bbfd01bd22638560a1b2c9ab6176286973527b8c Mon Sep 17 00:00:00 2001 From: Natallia-Harshunova <169041964+Natallia-Harshunova@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:54:49 +0200 Subject: [PATCH] feat(18228): add pricing page to atlas app (#770) * feat(18228): add pricing page --------- Co-authored-by: Natallia Harshunova --- package.json | 4 +- pnpm-lock.yaml | 20 ++-- src/core/api/subscription.ts | 16 +++ src/core/auth/types.ts | 1 + .../localization/translations/en/common.json | 6 ++ src/core/router/routes.tsx | 9 ++ .../PaymentPlanButton.module.css | 13 +++ .../PaymentPlanButton/PaymentPlanButton.tsx | 40 ++++++++ .../PaymentPlanCard.module.css | 99 +++++++++++++++++++ .../PaymentPlanCard/PaymentPlanCard.tsx | 82 +++++++++++++++ .../components/PaymentPlanCard/contants.tsx | 14 +++ .../PaymentPlanCardFooter.tsx | 49 +++++++++ .../components/Price/Price.module.css | 24 +++++ .../subscriptions/components/Price/Price.tsx | 18 ++++ .../PricingContent/PricingContent.module.css | 49 +++++++++ .../PricingContent/PricingContent.tsx | 83 ++++++++++++++++ src/features/subscriptions/types.ts | 38 +++++++ src/views/About/About.tsx | 1 - src/views/Pricing/Pricing.tsx | 5 + 19 files changed, 558 insertions(+), 13 deletions(-) create mode 100644 src/core/api/subscription.ts create mode 100644 src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.module.css create mode 100644 src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.tsx create mode 100644 src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.module.css create mode 100644 src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.tsx create mode 100644 src/features/subscriptions/components/PaymentPlanCard/contants.tsx create mode 100644 src/features/subscriptions/components/PaymentPlanCardFooter/PaymentPlanCardFooter.tsx create mode 100644 src/features/subscriptions/components/Price/Price.module.css create mode 100644 src/features/subscriptions/components/Price/Price.tsx create mode 100644 src/features/subscriptions/components/PricingContent/PricingContent.module.css create mode 100644 src/features/subscriptions/components/PricingContent/PricingContent.tsx create mode 100644 src/features/subscriptions/types.ts create mode 100644 src/views/Pricing/Pricing.tsx diff --git a/package.json b/package.json index f597a4f7f..bb0dc9e00 100644 --- a/package.json +++ b/package.json @@ -83,10 +83,10 @@ "@deck.gl/mesh-layers": "^8.9.33", "@github/mini-throttle": "^2.1.1", "@homer0/deferred": "^3.0.5", - "@konturio/default-icons": "^2.3.2", + "@konturio/default-icons": "^2.4.0", "@konturio/default-theme": "~3.2.5", "@konturio/floating": "^1.1.2", - "@konturio/ui-kit": "^5.2.0", + "@konturio/ui-kit": "^5.3.0", "@loaders.gl/core": "^3.4.14", "@loaders.gl/loader-utils": "^3.4.14", "@loaders.gl/worker-utils": "^3.4.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d42f003ba..a9d6467f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,8 +41,8 @@ importers: specifier: ^3.0.5 version: 3.0.5 '@konturio/default-icons': - specifier: ^2.3.2 - version: 2.3.2(react@18.2.0) + specifier: ^2.4.0 + version: 2.4.0(react@18.2.0) '@konturio/default-theme': specifier: ~3.2.5 version: 3.2.5 @@ -50,8 +50,8 @@ importers: specifier: ^1.1.2 version: 1.1.2(clsx@1.2.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@konturio/ui-kit': - specifier: ^5.2.0 - version: 5.2.0(@egjs/hammerjs@2.0.17)(clsx@1.2.1)(component-emitter@1.3.1)(keycharm@0.4.0)(moment@2.30.1)(propagating-hammerjs@2.0.1(@egjs/hammerjs@2.0.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(uuid@8.3.2)(vis-util@5.0.7(@egjs/hammerjs@2.0.17)(component-emitter@1.3.1))(xss@1.0.15) + specifier: ^5.3.0 + version: 5.3.0(@egjs/hammerjs@2.0.17)(clsx@1.2.1)(component-emitter@1.3.1)(keycharm@0.4.0)(moment@2.30.1)(propagating-hammerjs@2.0.1(@egjs/hammerjs@2.0.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(uuid@8.3.2)(vis-util@5.0.7(@egjs/hammerjs@2.0.17)(component-emitter@1.3.1))(xss@1.0.15) '@loaders.gl/core': specifier: ^3.4.14 version: 3.4.14 @@ -1086,8 +1086,8 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@konturio/default-icons@2.3.2': - resolution: {integrity: sha512-tu4JS0EsqsXWtlb/pfzh00MihAQMqDVrv+oUh1XbYXnvhgTCH+2xH4m1SGXD+O/UGGFEWZHIMH0IMBl99iZRSg==} + '@konturio/default-icons@2.4.0': + resolution: {integrity: sha512-cD167SKUuj859tS82wi5KyurQSKipSsyxtdG6KFzgL5N+9Xv1i5YHfRYrJ1E4o2Sh4G3OJ7/aFLTQa0bcJ9/4w==} peerDependencies: react: ^18.2.0 @@ -1100,8 +1100,8 @@ packages: clsx: ^1.2.1 react: ^18.2.0 - '@konturio/ui-kit@5.2.0': - resolution: {integrity: sha512-HobxhYKTjrWOUDrcZxEO+VEt1JoBAbdsn0iggj/SOkWy98lYtIS2c6ToBQP0naqcAH/P4H1fODnQ94V1JRv5Bw==} + '@konturio/ui-kit@5.3.0': + resolution: {integrity: sha512-8G3Bo7Z366qFYsppARSMNBhxTwPcWgkaJqRZ6hZ5bRcc/K0w7qz8/k5NEeSiIuJEuWfufE6mg1sRYXHifMWLJg==} peerDependencies: clsx: ^1.2.1 react: ^18.2.0 @@ -7780,7 +7780,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@konturio/default-icons@2.3.2(react@18.2.0)': + '@konturio/default-icons@2.4.0(react@18.2.0)': dependencies: react: 18.2.0 @@ -7796,7 +7796,7 @@ snapshots: transitivePeerDependencies: - react-dom - '@konturio/ui-kit@5.2.0(@egjs/hammerjs@2.0.17)(clsx@1.2.1)(component-emitter@1.3.1)(keycharm@0.4.0)(moment@2.30.1)(propagating-hammerjs@2.0.1(@egjs/hammerjs@2.0.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(uuid@8.3.2)(vis-util@5.0.7(@egjs/hammerjs@2.0.17)(component-emitter@1.3.1))(xss@1.0.15)': + '@konturio/ui-kit@5.3.0(@egjs/hammerjs@2.0.17)(clsx@1.2.1)(component-emitter@1.3.1)(keycharm@0.4.0)(moment@2.30.1)(propagating-hammerjs@2.0.1(@egjs/hammerjs@2.0.17))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(uuid@8.3.2)(vis-util@5.0.7(@egjs/hammerjs@2.0.17)(component-emitter@1.3.1))(xss@1.0.15)': dependencies: '@floating-ui/react-dom': 1.3.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@konturio/floating': 1.1.2(clsx@1.2.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) diff --git a/src/core/api/subscription.ts b/src/core/api/subscription.ts new file mode 100644 index 000000000..d119d42f5 --- /dev/null +++ b/src/core/api/subscription.ts @@ -0,0 +1,16 @@ +import { apiClient } from '~core/apiClientInstance'; +import { configRepo } from '~core/config'; + +export type CurrentSubscription = { + id: string; + billingPlanId: string; + billingSubscriptionId: string; +}; + +export async function getCurrentUserSubscription() { + return await apiClient.get( + '/users/current_user/billing_subscription', + { appId: configRepo.get().id }, + true, + ); +} diff --git a/src/core/auth/types.ts b/src/core/auth/types.ts index 5c42c45f4..7aa8bea84 100644 --- a/src/core/auth/types.ts +++ b/src/core/auth/types.ts @@ -56,6 +56,7 @@ export const AppFeature = { REFERENCE_AREA: 'reference_area', LLM_ANALYTICS: 'llm_analytics', MAP: 'map', + SUBSCRIPTION: 'subscription', } as const; export type AppFeatureType = (typeof AppFeature)[keyof typeof AppFeature]; diff --git a/src/core/localization/translations/en/common.json b/src/core/localization/translations/en/common.json index 13739f355..d8c85708a 100644 --- a/src/core/localization/translations/en/common.json +++ b/src/core/localization/translations/en/common.json @@ -316,6 +316,12 @@ "connect": "Could not connect to authentication service" } }, + "subscription": { + "title": "Plans & Pricing", + "price_summary": "* Billed as {{pricePerYear}} once yearly", + "unauthorized_button": "Sign in to subscribe", + "current_plan_button": "Current plan" + }, "reports": { "title": "Disaster Ninja Reports", "no_data": "No data for this report", diff --git a/src/core/router/routes.tsx b/src/core/router/routes.tsx index 90857e479..31da8d774 100644 --- a/src/core/router/routes.tsx +++ b/src/core/router/routes.tsx @@ -6,6 +6,7 @@ import { Prefs24, User24, Reports16, + Diamond24, } from '@konturio/default-icons'; import { i18n } from '~core/localization'; import { AppFeature } from '~core/auth/types'; @@ -13,6 +14,7 @@ import { configRepo } from '~core/config'; import { PagesDocument } from '~core/pages'; import { goTo } from './goTo'; import type { AppRouterConfig } from './types'; +const { PricingPage } = lazily(() => import('~views/Pricing/Pricing')); const { MapPage } = lazily(() => import('~views/Map/Map')); const { ReportsPage } = lazily(() => import('~views/Reports/Reports')); const { ReportPage } = lazily(() => import('~views/Report/Report')); @@ -65,6 +67,13 @@ export const routerConfig: AppRouterConfig = { view: , requiredFeature: AppFeature.APP_LOGIN, }, + { + slug: 'pricing', + title: i18n.t('subscription.title'), + icon: , + view: , + requiredFeature: AppFeature.SUBSCRIPTION, + }, { slug: 'about', title: i18n.t('modes.about'), diff --git a/src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.module.css b/src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.module.css new file mode 100644 index 000000000..d326b2a63 --- /dev/null +++ b/src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.module.css @@ -0,0 +1,13 @@ +.paymentPlanButton { + background-color: var(--strong-color); + + --subscribe-btn-hover: #269b00; + + &:global(.premium) { + --subscribe-btn-hover: var(--accent-strong-up); + } + + &:hover { + background-color: var(--subscribe-btn-hover); + } +} diff --git a/src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.tsx b/src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.tsx new file mode 100644 index 000000000..29afa35c8 --- /dev/null +++ b/src/features/subscriptions/components/PaymentPlanButton/PaymentPlanButton.tsx @@ -0,0 +1,40 @@ +import { Button } from '@konturio/ui-kit'; +import React, { memo } from 'react'; +import clsx from 'clsx'; +import { i18n } from '~core/localization'; +import s from './PaymentPlanButton.module.css'; +import type { CurrentSubscription } from '~core/api/subscription'; +import type { PaymentPlan } from '~features/subscriptions/types'; + +export type PaymentPlanButtonProps = { + plan: PaymentPlan; + isUserAuthorized: boolean; + onUnauthorizedUserClick: () => void; + currentSubscription: CurrentSubscription | null; + style: string; +}; + +const PaymentPlanButton = memo(function PaymentPlanButton({ + plan, + isUserAuthorized, + onUnauthorizedUserClick, + currentSubscription, + style, +}: PaymentPlanButtonProps) { + if (!isUserAuthorized) { + return ( + + ); + } + if (plan.id === currentSubscription?.id) { + return ; + } + return ; +}); + +export default PaymentPlanButton; diff --git a/src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.module.css b/src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.module.css new file mode 100644 index 000000000..6ea22ae79 --- /dev/null +++ b/src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.module.css @@ -0,0 +1,99 @@ +.planCard { + border: 1px solid var(--faint-weak-up, #d2d5d8); + border-radius: var(--double-unit, 16px); + padding: 48px 40px 24px; + height: 578px; + max-width: 400px; + min-width: 220px; + display: flex; + position: relative; + flex-direction: column; + + --strong-color: var(--success-strong); + --plan-type-bg-color: #def5df; + + &:global(.premium) { + --strong-color: var(--accent-strong); + --plan-type-bg-color: #d8effc; + + background-color: var(--accent-weak); + border-color: var(--strong-color); + } +} + +.planIcon { + margin-right: var(--unit); +} + +.planName { + position: absolute; + padding: var(--unit) var(--double-unit); + border-radius: 18px; + right: 40px; + top: -18px; + background-color: var(--plan-type-bg-color); + color: var(--strong-color); + display: flex; + align-items: center; +} + +.initialPrice { + color: var(--faint-strong, #9ea5ab); + text-decoration: line-through; + margin-bottom: var(--double-unit); +} + +.price { + margin-bottom: var(--double-unit); +} + +.perMonth { + font-size: 14px; + color: #888; + margin-left: 5px; +} + +.planDescription { + color: var(--faint-strong); + margin-bottom: var(--double-unit); +} + +.buttonWrapper { + margin-bottom: 24px; +} + +.cancelButton { + position: relative; + bottom: -11px; +} + +.highlights { + list-style: none; + padding: 0; + margin: 0; + + & li { + margin: 5px 0; + } +} + +.highlight { + display: flex; + align-items: center; +} + +.highlightIcon { + margin-right: var(--double-unit); + color: var(--strong-color); +} + +.footerWrapper { + display: flex; + justify-content: flex-end; + margin-top: auto; + color: var(--faint-strong); + + & span { + color: var(--faint-strong-down); + } +} diff --git a/src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.tsx b/src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.tsx new file mode 100644 index 000000000..38e532c2c --- /dev/null +++ b/src/features/subscriptions/components/PaymentPlanCard/PaymentPlanCard.tsx @@ -0,0 +1,82 @@ +import React, { memo, useMemo } from 'react'; +import { Heading, Text } from '@konturio/ui-kit'; +import { Finish24 } from '@konturio/default-icons'; +import clsx from 'clsx'; +import { Price } from '~features/subscriptions/components/Price/Price'; +import PaymentPlanButton from '~features/subscriptions/components/PaymentPlanButton/PaymentPlanButton'; +import PaymentPlanCardFooter from '~features/subscriptions/components/PaymentPlanCardFooter/PaymentPlanCardFooter'; +import s from './PaymentPlanCard.module.css'; +import { PLANS_STYLE_CONFIG } from './contants'; +import type { PaymentPlan } from '~features/subscriptions/types'; +import type { CurrentSubscription } from '~core/api/subscription'; + +export type PaymentPlanCardProps = { + plan: PaymentPlan; + currentBillingCycleId: string; + currentSubscription: CurrentSubscription | null; + isUserAuthorized: boolean; + onUnauthorizedUserClick: () => void; +}; + +const PaymentPlanCard = memo(function PaymentPlanCard({ + plan, + currentBillingCycleId, + currentSubscription, + isUserAuthorized, + onUnauthorizedUserClick, +}: PaymentPlanCardProps) { + const styleConfig = PLANS_STYLE_CONFIG[plan.style]; + + const billingOption = useMemo( + () => plan.billingCycles.find((option) => option.id === currentBillingCycleId), + [plan.billingCycles, currentBillingCycleId], + ); + + return ( +
+
+ {styleConfig.icon()} + + {plan.name} + +
+ {billingOption?.initialPricePerMonth && ( +
${billingOption.initialPricePerMonth}
+ )} + {billingOption?.pricePerMonth && ( + + )} + + {plan.description} + +
+ +
+
    + {plan.highlights.map((highlight, index) => ( +
  • + + {highlight} +
  • + ))} +
+ +
+ +
+
+ ); +}); + +export default PaymentPlanCard; diff --git a/src/features/subscriptions/components/PaymentPlanCard/contants.tsx b/src/features/subscriptions/components/PaymentPlanCard/contants.tsx new file mode 100644 index 000000000..280aafb0e --- /dev/null +++ b/src/features/subscriptions/components/PaymentPlanCard/contants.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { FavAdded16 } from '@konturio/default-icons'; +import s from './PaymentPlanCard.module.css'; + +export const PLANS_STYLE_CONFIG = { + basic: { + className: '', // Uses default styles + icon: () => null, + }, + premium: { + className: 'premium', + icon: () => , + }, +}; diff --git a/src/features/subscriptions/components/PaymentPlanCardFooter/PaymentPlanCardFooter.tsx b/src/features/subscriptions/components/PaymentPlanCardFooter/PaymentPlanCardFooter.tsx new file mode 100644 index 000000000..79590575c --- /dev/null +++ b/src/features/subscriptions/components/PaymentPlanCardFooter/PaymentPlanCardFooter.tsx @@ -0,0 +1,49 @@ +import { Text } from '@konturio/ui-kit'; +import React, { memo } from 'react'; +import { i18n } from '~core/localization'; +import type { BillingCycle, PaymentPlan } from '~features/subscriptions/types'; +import type { CurrentSubscription } from '~core/api/subscription'; + +export type PaymentPlanCardFooterProps = { + plan: PaymentPlan; + isUserAuthorized: boolean; + currentSubscription: CurrentSubscription | null; + billingOption?: BillingCycle; +}; + +const PaymentPlanCardFooter = memo(function PaymentPlanCardFooter({ + plan, + isUserAuthorized, + currentSubscription, + billingOption, +}: PaymentPlanCardFooterProps) { + // Postpone cancel button rendering till next pr + // if ( + // isUserAuthorized && + // plan.id === currentSubscription?.id + // ) { + // return ( + // + // ); + // } + if (billingOption?.pricePerYear) { + return ( + + {i18n.t('subscription.price_summary', { + pricePerYear: billingOption.pricePerYear, + })} + + ); + } + + return null; +}); + +export default PaymentPlanCardFooter; diff --git a/src/features/subscriptions/components/Price/Price.module.css b/src/features/subscriptions/components/Price/Price.module.css new file mode 100644 index 000000000..34f0b8f4c --- /dev/null +++ b/src/features/subscriptions/components/Price/Price.module.css @@ -0,0 +1,24 @@ +.priceWrap { + display: flex; + align-items: baseline; + color: var(--base-strong); +} + +.dollarSign { + align-self: flex-start; + font-weight: 600; + line-height: 20px; + font-size: 16px; + margin-right: var(--half-unit); +} + +.amount { + font-weight: 500; + line-height: 40px; + font-size: 52px; +} + +.perMonth { + margin-left: var(--unit); + color: var(--text-faint-strong); +} diff --git a/src/features/subscriptions/components/Price/Price.tsx b/src/features/subscriptions/components/Price/Price.tsx new file mode 100644 index 000000000..5e570a78b --- /dev/null +++ b/src/features/subscriptions/components/Price/Price.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import clsx from 'clsx'; +import s from './Price.module.css'; + +export type PriceProps = { + amount: number; + className: string; +}; + +export function Price({ amount, className }: PriceProps) { + return ( +
+
$
+
{amount}
+
/mo*
+
+ ); +} diff --git a/src/features/subscriptions/components/PricingContent/PricingContent.module.css b/src/features/subscriptions/components/PricingContent/PricingContent.module.css new file mode 100644 index 000000000..52cd86cb2 --- /dev/null +++ b/src/features/subscriptions/components/PricingContent/PricingContent.module.css @@ -0,0 +1,49 @@ +.pricingWrap { + width: 100%; + display: flex; + overflow-y: auto; +} + +.pricingPlans { + padding: 20px; + margin: auto; +} + +.togglerSwitch { + display: flex; + align-items: center; + margin-bottom: 32px; + margin-left: calc(var(--unit) * -1); + + &.withOffLabel { + margin-left: 0; + } +} + +.togglerLabel { + font-weight: 600; + font-size: 16px; + line-height: 20px; + color: var(--faint-strong-up); + + &.active { + color: var(--base-strong); + } +} + +.note { + background-color: var(--faint-weak); + color: var(--text-faint-strong); + padding: 2px 4px 4px 4px; + border-radius: var(--border-radius); + line-height: 12px; + font-size: 12px; + font-weight: 500; +} + +.plans { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 40px; +} diff --git a/src/features/subscriptions/components/PricingContent/PricingContent.tsx b/src/features/subscriptions/components/PricingContent/PricingContent.tsx new file mode 100644 index 000000000..e132c1b7f --- /dev/null +++ b/src/features/subscriptions/components/PricingContent/PricingContent.tsx @@ -0,0 +1,83 @@ +import clsx from 'clsx'; +import React, { useCallback, useState } from 'react'; +import { Heading, Toggler } from '@konturio/ui-kit'; +import usePromise from 'react-promise-suspense'; +import { configRepo } from '~core/config'; +import { FeatureFlag } from '~core/shared_state'; +import { i18n } from '~core/localization'; +import { getCurrentUserSubscription } from '~core/api/subscription'; +import PaymentPlanCard from '~features/subscriptions/components/PaymentPlanCard/PaymentPlanCard'; +import { goTo } from '~core/router/goTo'; +import s from './PricingContent.module.css'; +import type { SubscriptionsConfig } from '~features/subscriptions/types'; + +const togglerInitialValue = 'year'; + +export function PricingContent() { + const user = configRepo.get().user; + const currentSubscription = usePromise(() => { + if (!user) { + return Promise.resolve(null); + } + return getCurrentUserSubscription(); + }, []); + + const config = configRepo.get().features[ + FeatureFlag.SUBSCRIPTION + ] as SubscriptionsConfig; + + const [currentBillingCycleID, setCurrentBillingCycleID] = useState<'month' | 'year'>( + togglerInitialValue, + ); + + const [monthlyPlanConfig, annuallyPlanConfig] = config.billingCyclesDetails; + + const onTogglerChange = useCallback(() => { + setCurrentBillingCycleID((prev) => (prev === 'month' ? 'year' : 'month')); + }, []); + + const onUnauthorizedUserClick = useCallback(() => goTo('/profile'), []); + + return ( +
+
+ {i18n.t('subscription.title')} +
+ {monthlyPlanConfig.note && ( +
{monthlyPlanConfig.note}
+ )} + + {annuallyPlanConfig.note && ( +
{annuallyPlanConfig.note}
+ )} +
+
+ {config.plans.map((plan) => ( + + ))} +
+
+
+ ); +} diff --git a/src/features/subscriptions/types.ts b/src/features/subscriptions/types.ts new file mode 100644 index 000000000..4f96f9964 --- /dev/null +++ b/src/features/subscriptions/types.ts @@ -0,0 +1,38 @@ +export interface BillingCycleDetails { + id: 'month' | 'year'; + name: string; + note: string | null; +} + +interface BillingMethod { + id: string; + billingPlanId: string; +} + +export interface BillingCycle { + id: string; + pricePerMonth: number; + initialPricePerMonth: number | null; + pricePerYear: number | null; + billingMethods: BillingMethod[]; +} + +export interface PaymentPlan { + id: string; + name: string; + description: string; + style: 'basic' | 'premium'; + highlights: string[]; + billingCycles: BillingCycle[]; +} + +export interface BillingMethodDetails { + id: string; + clientId: string; +} + +export interface SubscriptionsConfig { + billingMethodsDetails: BillingMethodDetails[]; + billingCyclesDetails: BillingCycleDetails[]; + plans: PaymentPlan[]; +} diff --git a/src/views/About/About.tsx b/src/views/About/About.tsx index 2874eab48..45d67fa93 100644 --- a/src/views/About/About.tsx +++ b/src/views/About/About.tsx @@ -1,6 +1,5 @@ import { useAtom } from '@reatom/react-v2'; import { PagesDocument } from '~core/pages'; -import { configRepo } from '~core/config'; import { featureFlagsAtom, FeatureFlag } from '~core/shared_state'; const defaultDocument = [ diff --git a/src/views/Pricing/Pricing.tsx b/src/views/Pricing/Pricing.tsx new file mode 100644 index 000000000..07577308b --- /dev/null +++ b/src/views/Pricing/Pricing.tsx @@ -0,0 +1,5 @@ +import { PricingContent } from '~features/subscriptions/components/PricingContent/PricingContent'; + +export function PricingPage() { + return ; +}