optimize payment process (#3517)
This commit is contained in:
@@ -51,7 +51,7 @@ export const navbarWidth = '64px';
|
||||
const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
const router = useRouter();
|
||||
const { Loading } = useLoading();
|
||||
const { loading, feConfigs, isNotSufficientModal } = useSystemStore();
|
||||
const { loading, feConfigs, notSufficientModalType } = useSystemStore();
|
||||
const { isPc } = useSystem();
|
||||
const { userInfo, isUpdateNotification, setIsUpdateNotification } = useUserStore();
|
||||
const { setUserDefaultLng } = useI18nLng();
|
||||
@@ -121,7 +121,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
{!!userInfo && <UpdateInviteModal />}
|
||||
{isNotSufficientModal && <NotSufficientModal />}
|
||||
{notSufficientModalType && <NotSufficientModal type={notSufficientModalType} />}
|
||||
{!!userInfo && <SystemMsgModal />}
|
||||
{showUpdateNotification && (
|
||||
<UpdateNotification onClose={() => setIsUpdateNotification(false)} />
|
||||
|
||||
@@ -1,35 +1,148 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { Box, Button, Flex, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
|
||||
import { NotSufficientModalType, useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import ExtraPlan from '@/pages/price/components/ExtraPlan';
|
||||
import StandardPlan from '@/pages/price/components/Standard';
|
||||
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
const NotSufficientModal = () => {
|
||||
const NotSufficientModal = ({ type }: { type: NotSufficientModalType }) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { setIsNotSufficientModal } = useSystemStore();
|
||||
const { setNotSufficientModalType } = useSystemStore();
|
||||
|
||||
const onClose = () => setIsNotSufficientModal(false);
|
||||
const onClose = () => setNotSufficientModalType(undefined);
|
||||
|
||||
const {
|
||||
isOpen: isRechargeModalOpen,
|
||||
onOpen: onRechargeModalOpen,
|
||||
onClose: onRechargeModalClose
|
||||
} = useDisclosure();
|
||||
|
||||
const textMap = {
|
||||
[TeamErrEnum.aiPointsNotEnough]: t('common:support.wallet.Not sufficient'),
|
||||
[TeamErrEnum.datasetSizeNotEnough]: t('common:support.wallet.Dataset_not_sufficient'),
|
||||
[TeamErrEnum.datasetAmountNotEnough]: t('common:support.wallet.Dataset_amount_not_sufficient'),
|
||||
[TeamErrEnum.teamMemberOverSize]: t('common:support.wallet.Team_member_over_size'),
|
||||
[TeamErrEnum.appAmountNotEnough]: t('common:support.wallet.App_amount_not_sufficient')
|
||||
};
|
||||
|
||||
return (
|
||||
<MyModal isOpen iconSrc="common/confirm/deleteTip" title={t('common:common.Warning')}>
|
||||
<ModalBody>{t('common:support.wallet.Not sufficient')}</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
router.push('/account/info');
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('common:support.wallet.To read plan')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
<>
|
||||
<MyModal
|
||||
isOpen
|
||||
iconSrc="common/confirm/deleteTip"
|
||||
title={t('common:common.Warning')}
|
||||
w={'420px'}
|
||||
>
|
||||
<ModalBody>{textMap[type]}</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onRechargeModalOpen();
|
||||
}}
|
||||
>
|
||||
{t('common:support.wallet.To read plan')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
|
||||
{isRechargeModalOpen && (
|
||||
<RechargeModal onClose={onRechargeModalClose} onPaySuccess={onClose} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotSufficientModal;
|
||||
|
||||
const RechargeModal = ({
|
||||
onClose,
|
||||
onPaySuccess
|
||||
}: {
|
||||
onClose: () => void;
|
||||
onPaySuccess: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { teamPlanStatus } = useUserStore();
|
||||
|
||||
const planName = useMemo(() => {
|
||||
if (!teamPlanStatus?.standard?.currentSubLevel) return '';
|
||||
return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label;
|
||||
}, [teamPlanStatus?.standard?.currentSubLevel]);
|
||||
|
||||
const [tab, setTab] = useState<'standard' | 'extra'>('standard');
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
iconSrc="common/wallet"
|
||||
iconColor={'primary.600'}
|
||||
title={t('common:user.Pay')}
|
||||
onClose={onClose}
|
||||
isCentered
|
||||
minW={['100%', '1200px']}
|
||||
minH={['100%', '800px']}
|
||||
>
|
||||
<ModalBody px={'52px'}>
|
||||
<Flex alignItems={'center'} mb={6}>
|
||||
<FormLabel fontSize={'16px'} fontWeight={'medium'}>
|
||||
{t('common:support.wallet.subscription.Current plan')}
|
||||
</FormLabel>
|
||||
<Box fontSize={'14px'} ml={5} color={'myGray.900'}>
|
||||
{t(planName as any)}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems={'center'} mb={6}>
|
||||
<FormLabel fontSize={'16px'} fontWeight={'medium'}>
|
||||
{t('common:info.resource')}
|
||||
</FormLabel>
|
||||
<Flex fontSize={'14px'} ml={5} color={'myGray.900'}>
|
||||
<Box>{`${t('common:support.user.team.Dataset usage')}:`}</Box>
|
||||
<Box
|
||||
ml={2}
|
||||
>{`${teamPlanStatus?.usedDatasetSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`}</Box>
|
||||
<Box ml={5}>{`${t('common:support.wallet.subscription.AI points usage')}:`}</Box>
|
||||
<Box
|
||||
ml={2}
|
||||
>{`${Math.round(teamPlanStatus?.usedPoints || 0)} / ${teamPlanStatus?.totalPoints || t('account_info:unlimited')}`}</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<FillRowTabs
|
||||
list={[
|
||||
{ label: t('common:support.wallet.subscription.Sub plan'), value: 'standard' },
|
||||
{ label: t('common:support.wallet.subscription.Extra plan'), value: 'extra' }
|
||||
]}
|
||||
value={tab}
|
||||
onChange={(e) => {
|
||||
setTab(e as 'standard' | 'extra');
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
mt={3}
|
||||
p={8}
|
||||
bg={'myGray.50'}
|
||||
border={'1px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
rounded={'12px'}
|
||||
>
|
||||
{tab === 'standard' ? (
|
||||
<StandardPlan standardPlan={teamPlanStatus?.standard} onPaySuccess={onPaySuccess} />
|
||||
) : (
|
||||
<ExtraPlan onPaySuccess={onPaySuccess} />
|
||||
)}
|
||||
</Box>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ import { TeamContext, TeamModalContextProvider } from './components/context';
|
||||
import dynamic from 'next/dynamic';
|
||||
import TeamTagModal from '@/components/support/user/team/TeamTagModal';
|
||||
import MemberTable from './components/MemberTable';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
const InviteModal = dynamic(() => import('./components/InviteModal'));
|
||||
const PermissionManage = dynamic(() => import('./components/PermissionManage/index'));
|
||||
@@ -41,7 +42,7 @@ const Team = () => {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { userInfo, teamPlanStatus } = useUserStore();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { feConfigs, setNotSufficientModalType } = useSystemStore();
|
||||
|
||||
const {
|
||||
myTeams,
|
||||
@@ -221,10 +222,11 @@ const Team = () => {
|
||||
) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Over Max Member Tip', {
|
||||
title: t('common:user.team.Over Max Member Tip', {
|
||||
max: teamPlanStatus.standardConstants.maxTeamMember
|
||||
})
|
||||
});
|
||||
setNotSufficientModalType(TeamErrEnum.teamMemberOverSize);
|
||||
} else {
|
||||
onOpenInvite();
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ const ApiDatasetForm = ({
|
||||
</Flex>
|
||||
<Input
|
||||
bg={'myWhite.600'}
|
||||
placeholder={'Token'}
|
||||
placeholder={'User ID'}
|
||||
maxLength={200}
|
||||
{...register('yuqueServer.userId', { required: true })}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Flex, Grid, Button } from '@chakra-ui/react';
|
||||
import { Box, Flex, Grid, Button, VStack } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
@@ -11,7 +11,7 @@ import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal';
|
||||
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||
|
||||
const ExtraPlan = () => {
|
||||
const ExtraPlan = ({ onPaySuccess }: { onPaySuccess?: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { subPlans } = useSystemStore();
|
||||
@@ -108,19 +108,8 @@ const ExtraPlan = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
mt={['40px', '100px']}
|
||||
flexDirection={'column'}
|
||||
alignItems={'center'}
|
||||
position={'relative'}
|
||||
>
|
||||
<Box id={'extra-plan'} fontWeight={'bold'} fontSize={['24px', '36px']} color={'myGray.900'}>
|
||||
{t('common:support.wallet.subscription.Extra plan')}
|
||||
</Box>
|
||||
<Box mt={2} mb={8} color={'myGray.600'} fontSize={'md'}>
|
||||
{t('common:support.wallet.subscription.Extra plan tip')}
|
||||
</Box>
|
||||
<Grid mt={8} gridTemplateColumns={['1fr', '1fr 1fr']} gap={5} w={['100%', 'auto']}>
|
||||
<VStack>
|
||||
<Grid gridTemplateColumns={['1fr', '1fr 1fr']} gap={5} w={['100%', 'auto']}>
|
||||
<Box
|
||||
bg={'rgba(255, 255, 255, 0.90)'}
|
||||
px={'32px'}
|
||||
@@ -284,8 +273,8 @@ const ExtraPlan = () => {
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{!!qrPayData && <QRCodePayModal {...qrPayData} />}
|
||||
</Flex>
|
||||
{!!qrPayData && <QRCodePayModal onSuccess={onPaySuccess} {...qrPayData} />}
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@ export enum PackageChangeStatusEnum {
|
||||
|
||||
const Standard = ({
|
||||
standardPlan: myStandardPlan,
|
||||
refetchTeamSubPlan
|
||||
onPaySuccess
|
||||
}: {
|
||||
standardPlan?: TeamSubSchema;
|
||||
refetchTeamSubPlan: () => void;
|
||||
onPaySuccess?: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -78,14 +78,6 @@ const Standard = ({
|
||||
return (
|
||||
<>
|
||||
<Flex flexDirection={'column'} alignItems={'center'} position={'relative'}>
|
||||
<Box fontWeight={'600'} color={'myGray.900'} fontSize={['24px', '36px']}>
|
||||
{t('common:support.wallet.subscription.Sub plan')}
|
||||
</Box>
|
||||
<Box mt={8} mb={10} fontWeight={'500'} color={'myGray.600'} fontSize={'md'}>
|
||||
{t('common:support.wallet.subscription.Sub plan tip', {
|
||||
title: feConfigs?.systemTitle
|
||||
})}
|
||||
</Box>
|
||||
<Box>
|
||||
<RowTabs
|
||||
list={[
|
||||
@@ -271,15 +263,13 @@ const Standard = ({
|
||||
</Grid>
|
||||
|
||||
{!!qrPayData && packageChange && (
|
||||
<QRCodePayModal tip={packagePayTextMap[packageChange]} {...qrPayData} />
|
||||
<QRCodePayModal
|
||||
tip={packagePayTextMap[packageChange]}
|
||||
onSuccess={onPaySuccess}
|
||||
{...qrPayData}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<HStack mt={8} color={'blue.700'} ml={8}>
|
||||
<MyIcon name={'infoRounded'} w={'1rem'} />
|
||||
<Box fontSize={'sm'} fontWeight={'500'}>
|
||||
{t('user:bill.standard_valid_tip')}
|
||||
</Box>
|
||||
</HStack>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { Box, Flex, HStack, VStack } from '@chakra-ui/react';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { getTeamPlanStatus } from '@/web/support/user/team/api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@@ -12,17 +12,18 @@ import FAQ from './components/FAQ';
|
||||
import { getToken } from '@/web/support/user/auth';
|
||||
import Script from 'next/script';
|
||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
const PriceBox = () => {
|
||||
const { userInfo } = useUserStore();
|
||||
const { t } = useTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const { data: teamSubPlan, refetch: refetchTeamSubPlan } = useQuery(
|
||||
['getTeamPlanStatus'],
|
||||
getTeamPlanStatus,
|
||||
{
|
||||
enabled: !!getToken() || !!userInfo
|
||||
}
|
||||
);
|
||||
const { data: teamSubPlan } = useQuery(['getTeamPlanStatus'], getTeamPlanStatus, {
|
||||
enabled: !!getToken() || !!userInfo
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -39,12 +40,39 @@ const PriceBox = () => {
|
||||
backgroundRepeat={'no-repeat'}
|
||||
>
|
||||
{/* standard sub */}
|
||||
<StandardPlan
|
||||
standardPlan={teamSubPlan?.standard}
|
||||
refetchTeamSubPlan={refetchTeamSubPlan}
|
||||
/>
|
||||
<VStack>
|
||||
<Box fontWeight={'600'} color={'myGray.900'} fontSize={['24px', '36px']}>
|
||||
{t('common:support.wallet.subscription.Sub plan')}
|
||||
</Box>
|
||||
<Box mt={8} mb={10} fontWeight={'500'} color={'myGray.600'} fontSize={'md'}>
|
||||
{t('common:support.wallet.subscription.Sub plan tip', {
|
||||
title: feConfigs?.systemTitle
|
||||
})}
|
||||
</Box>
|
||||
<StandardPlan standardPlan={teamSubPlan?.standard} />
|
||||
<HStack mt={8} color={'blue.700'} ml={8}>
|
||||
<MyIcon name={'infoRounded'} w={'1rem'} />
|
||||
<Box fontSize={'sm'} fontWeight={'500'}>
|
||||
{t('user:bill.standard_valid_tip')}
|
||||
</Box>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
<ExtraPlan />
|
||||
{/* extra plan */}
|
||||
<VStack mt={['40px', '100px']} mb={8}>
|
||||
<Box
|
||||
id={'extra-plan'}
|
||||
fontWeight={'bold'}
|
||||
fontSize={['24px', '36px']}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{t('common:support.wallet.subscription.Extra plan')}
|
||||
</Box>
|
||||
<Box mt={2} mb={8} color={'myGray.600'} fontSize={'md'}>
|
||||
{t('common:support.wallet.subscription.Extra plan tip')}
|
||||
</Box>
|
||||
<ExtraPlan />
|
||||
</VStack>
|
||||
|
||||
{/* points */}
|
||||
<PointsCard />
|
||||
|
||||
@@ -220,7 +220,7 @@ export const streamFetch = ({
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.error) {
|
||||
if (parseJson.statusText === TeamErrEnum.aiPointsNotEnough) {
|
||||
useSystemStore.getState().setIsNotSufficientModal(true);
|
||||
useSystemStore.getState().setNotSufficientModalType(TeamErrEnum.aiPointsNotEnough);
|
||||
}
|
||||
errMsg = getErrText(parseJson, '流响应错误');
|
||||
}
|
||||
|
||||
@@ -120,8 +120,13 @@ function responseError(err: any) {
|
||||
|
||||
return Promise.reject({ message: '无权操作' });
|
||||
}
|
||||
if (err?.statusText === TeamErrEnum.aiPointsNotEnough) {
|
||||
useSystemStore.getState().setIsNotSufficientModal(true);
|
||||
if (
|
||||
err?.statusText === TeamErrEnum.aiPointsNotEnough ||
|
||||
err?.statusText === TeamErrEnum.datasetSizeNotEnough ||
|
||||
err?.statusText === TeamErrEnum.datasetAmountNotEnough ||
|
||||
err?.statusText === TeamErrEnum.appAmountNotEnough
|
||||
) {
|
||||
useSystemStore.getState().setNotSufficientModalType(err.statusText);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
if (err?.response?.data) {
|
||||
|
||||
@@ -14,9 +14,17 @@ import { InitDateResponse } from '@/global/common/api/systemRes';
|
||||
import { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types';
|
||||
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
|
||||
import { defaultWhisperModel } from '@fastgpt/global/core/ai/model';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
type LoginStoreType = { provider: `${OAuthEnum}`; lastRoute: string; state: string };
|
||||
|
||||
export type NotSufficientModalType =
|
||||
| TeamErrEnum.datasetSizeNotEnough
|
||||
| TeamErrEnum.aiPointsNotEnough
|
||||
| TeamErrEnum.datasetAmountNotEnough
|
||||
| TeamErrEnum.teamMemberOverSize
|
||||
| TeamErrEnum.appAmountNotEnough;
|
||||
|
||||
type State = {
|
||||
initd: boolean;
|
||||
setInitd: () => void;
|
||||
@@ -34,8 +42,8 @@ type State = {
|
||||
gitStar: number;
|
||||
loadGitStar: () => Promise<void>;
|
||||
|
||||
isNotSufficientModal: boolean;
|
||||
setIsNotSufficientModal: (val: boolean) => void;
|
||||
notSufficientModalType: NotSufficientModalType | undefined;
|
||||
setNotSufficientModalType: (val: NotSufficientModalType | undefined) => void;
|
||||
|
||||
initDataBufferId?: string;
|
||||
feConfigs: FastGPTFeConfigsType;
|
||||
@@ -106,10 +114,10 @@ export const useSystemStore = create<State>()(
|
||||
} catch (error) {}
|
||||
},
|
||||
|
||||
isNotSufficientModal: false,
|
||||
setIsNotSufficientModal(val: boolean) {
|
||||
notSufficientModalType: undefined,
|
||||
setNotSufficientModalType(type: NotSufficientModalType | undefined) {
|
||||
set((state) => {
|
||||
state.isNotSufficientModal = val;
|
||||
state.notSufficientModalType = type;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user