| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- import { memo, useEffect } from 'react';
- import { Form } from 'antd';
- import { useTranslation } from 'react-i18next';
- import { useResponsive } from '@/hooks/useSize';
- import OrderSummary from './components/OrderSummary';
- import PayMethodCard from './components/PayMethodCard';
- import PlanCard from './components/PlanCard';
- import UserInfo from './components/UserInfo';
- import { useAction } from './useAction';
- import { useService } from './useService';
- import type { Plan } from './useService';
- const PRICING_FORM_ID = 'pricing-form';
- export interface PlanSelectorProps {
- value?: string;
- onChange?: (planId: string) => void;
- plans: Plan[];
- isMobile: boolean;
- }
- function PlanSelector({ value, onChange, plans, isMobile }: PlanSelectorProps) {
- return (
- <div
- data-count={plans.length}
- style={{ gridAutoRows: '1fr' }}
- className={
- isMobile
- ? 'flex-col-c gap-5'
- : 'grid grid-cols-4 gap-x-8 gap-y-4 [&[data-count="1"]]:grid-cols-3 [&[data-count="2"]]:grid-cols-3 [&[data-count="3"]]:grid-cols-3'
- }
- >
- {plans.map((plan) => (
- <PlanCard
- key={plan.id}
- id={plan.id}
- title={plan.title}
- subTitle={plan.subTitle}
- introduce={plan.introduce}
- tag={plan.tag}
- tagType={plan.tagType}
- isSelected={value === plan.id}
- onClick={() => onChange?.(plan.id)}
- />
- ))}
- </div>
- );
- }
- export interface PayMethodSelectorProps {
- value?: string;
- onChange?: (payType: string) => void;
- payMethods: API.PayTypeItem[];
- isMobile: boolean;
- }
- function PayMethodSelector({ value, onChange, payMethods, isMobile }: PayMethodSelectorProps) {
- return (
- <div
- className={
- isMobile
- ? 'flex-col-c gap-4'
- : 'flex flex-wrap justify-start gap-x-8 gap-y-4 [&>*]:flex-[1_1_calc(50%-1rem)] [&>*]:max-w-[calc(50%-1rem)]'
- }
- >
- {payMethods.map((item) => (
- <PayMethodCard
- key={item.payType}
- item={item}
- isSelected={value === item.payType}
- onClick={() => onChange?.(item.payType)}
- />
- ))}
- </div>
- );
- }
- const Pricing = memo(() => {
- const { t } = useTranslation();
- const { isMobile } = useResponsive();
- const [form] = Form.useForm<{ planId: string; payMethod: string }>();
- const { plans, payMethods } = useService();
- const { handlePayNow } = useAction();
- const planId = Form.useWatch('planId', form);
- const payMethod = Form.useWatch('payMethod', form);
- const selectedPlan = plans.find((p) => p.id === planId) ?? null;
- const selectedPayMethod = payMethod ?? null;
- useEffect(() => {
- if (plans.length === 0) return;
- const defaultPlan = plans.find((p) => p.isDefault);
- if (defaultPlan) {
- form.setFieldsValue({ planId: defaultPlan.id });
- }
- }, [plans, form]);
- const onFinish = (values: { planId: string; payMethod: string }) => {
- const plan = plans.find((p) => p.id === values.planId);
- if (!plan || !values.payMethod) return;
- handlePayNow(plan, values.payMethod);
- };
- return (
- <div className="flex items-start justify-center">
- <div className={`max-w-[1440px] w-full ${isMobile ? 'px-0' : 'px-[30px]'}`}>
- <div
- className={`bg-[#0F1116] px-5 sm:px-5 lg:px-[100px] py-[30px] flex flex-col ${isMobile ? 'gap-5 mt-0 pb-[30px]' : 'gap-10 my-[50px] rounded-[12px] pb-[100px]'}`}
- >
- <span
- className={`text-white font-semibold leading-[1.43] text-center uppercase ${isMobile ? 'text-[22px]' : 'text-[35px]'}`}
- >
- {t('pages.pricing.title')}
- </span>
- <UserInfo />
- <Form
- form={form}
- id={PRICING_FORM_ID}
- layout="vertical"
- onFinish={onFinish}
- className="flex flex-col gap-5"
- >
- <div className="flex flex-col gap-5">
- <span
- className={`text-white font-semibold leading-[1.43] ${isMobile ? 'text-[16px]' : 'text-[22px]'}`}
- >
- {t('pages.pricing.selecPlan')}
- </span>
- <Form.Item
- name="planId"
- rules={[
- {
- required: true,
- message: t('pages.pricing.pleaseSelectPlan'),
- },
- ]}
- className="mb-0 [&_.ant-form-item-explain]:mt-3"
- >
- <PlanSelector plans={plans} isMobile={isMobile} />
- </Form.Item>
- </div>
- <div className="flex flex-col gap-4 mt-1">
- <span
- className={`text-white font-semibold leading-[1.43] ${isMobile ? 'text-[16px]' : 'text-[22px]'}`}
- >
- {t('pages.pricing.selectPayMethod')}
- </span>
- <Form.Item
- name="payMethod"
- rules={[
- {
- required: true,
- message: t('pages.pricing.pleaseSelectPayMethod'),
- },
- ]}
- className="mb-0 [&_.ant-form-item-explain]:mt-3"
- >
- <PayMethodSelector payMethods={payMethods} isMobile={isMobile} />
- </Form.Item>
- </div>
- <OrderSummary
- formId={PRICING_FORM_ID}
- selectedPlan={selectedPlan}
- selectedPayMethod={selectedPayMethod}
- />
- </Form>
- </div>
- </div>
- </div>
- );
- });
- Pricing.displayName = 'Pricing';
- export default Pricing;
|