Skip to content

Commit

Permalink
style(costcenter): recharge discount (#5111)
Browse files Browse the repository at this point in the history
  • Loading branch information
xudaotutou authored Sep 26, 2024
1 parent b6053c1 commit 54a3bc2
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 194 deletions.
4 changes: 4 additions & 0 deletions frontend/packages/ui/src/theme/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const baseColors = {
800: '#005B9C',
900: '#004B82'
},
royalBlue: {
100: '#E1EAFF',
700: '#2B5FD9'
},
yellow: {
25: '#FFFDFA',
50: '#FFFAEB',
Expand Down
7 changes: 5 additions & 2 deletions frontend/providers/costcenter/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"Stripe Success": "pay with Stripe successfully",
"Stripe Cancel": "cancel to pay with Stripe",
"GPU Unit": "Card",
"port_unit": "",
"port_unit": "",
"Gpu valuation": "GPU Price Table",
"common valuation": "Basic Valuation",
"Billing Details": "Billing Details",
Expand Down Expand Up @@ -233,5 +233,8 @@
"duration": "duration",
"GPU": "GPU",
"usage": "usage",
"resource": "resource"
"resource": "resource",
"Double": "Double",
"first_recharge_tips": "Partial specifications can enjoy double the amount of the initial recharge.",
"first_recharge_title": "Double on First Recharge"
}
7 changes: 5 additions & 2 deletions frontend/providers/costcenter/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
"Stripe Success": "Stripe 支付完成",
"Stripe Cancel": "Stripe 支付取消",
"GPU Unit": "",
"port_unit": "",
"port_unit": "",
"Name": "名称",
"Price": "价格",
"Unit": "单位",
Expand Down Expand Up @@ -233,5 +233,8 @@
"duration": "时间",
"GPU": "GPU",
"usage": "用量",
"resource": "资源"
"resource": "资源",
"Double": "双倍",
"first_recharge_tips": "部分规格首次充值可享双倍赠送金额",
"first_recharge_title": "首充双倍"
}
187 changes: 135 additions & 52 deletions frontend/providers/costcenter/src/components/RechargeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { default as CurrencySymbol, default as Currencysymbol } from '@/componen
import OuterLink from '@/components/outerLink';
import { useCustomToast } from '@/hooks/useCustomToast';
import useEnvStore from '@/stores/env';
import useSessionStore from '@/stores/session';
import { ApiResp } from '@/types/api';
import { Pay, Payment } from '@/types/payment';
import { getFavorable } from '@/utils/favorable';
import { deFormatMoney, formatMoney } from '@/utils/format';
import {
Box,
Expand All @@ -29,13 +29,16 @@ import {
Text,
useDisclosure
} from '@chakra-ui/react';
import { MyTooltip } from '@sealos/ui';
import { Stripe } from '@stripe/stripe-js';
import { useMutation, useQuery } from '@tanstack/react-query';
import type { AxiosInstance } from 'axios';
import { isNumber } from 'lodash';
import { useTranslation } from 'next-i18next';
import { QRCodeSVG } from 'qrcode.react';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
import GiftIcon from './icons/GiftIcon';
import HelpIcon from './icons/HelpIcon';
const StripeForm = (props: {
tradeNO?: string;
complete: number;
Expand Down Expand Up @@ -122,10 +125,12 @@ const BonusBox = (props: {
onClick: () => void;
selected: boolean;
bouns: number;
isFirst?: boolean;
amount: number;
}) => {
const { t } = useTranslation();
const currency = useEnvStore((s) => s.currency);

return (
<Flex
width="140px"
Expand All @@ -151,26 +156,67 @@ const BonusBox = (props: {
props.onClick();
}}
>
<Flex
position={'absolute'}
minW={'max-content'}
left="78px"
top="4px"
px={'13.5px'}
py={'2.5px'}
color={'purple.600'}
background="purple.100"
alignItems={'center'}
borderRadius="10px 10px 10px 0px"
zIndex={'99'}
fontStyle="normal"
fontWeight="500"
fontSize="12px"
>
<Text mr="4px">{t('Bonus')}</Text>
<CurrencySymbol boxSize={'10px'} mr={'2px'} />
<Text> {props.bouns}</Text>
</Flex>
{props.isFirst ? (
<Flex
position={'absolute'}
minW={'max-content'}
right={'-6px'}
top="-18px"
color={'royalBlue.700'}
background="royalBlue.100"
alignItems={'center'}
borderRadius="2px"
zIndex={'99'}
fontStyle="normal"
fontWeight="500"
fontSize="12px"
_before={{
position: 'absolute',
inset: 'auto',
borderRadius: '2px',
width: '50px',
height: '50px',
content: '""',
transform: 'rotate(45deg)',
zIndex: '-1',
bgColor: 'royalBlue.100'
}}
w="50px"
h="50px"
align={'center'}
justify={'center'}
>
<Flex flexDirection={'column'} align={'center'}>
<Text>{t('Double')}!</Text>
<Flex align={'center'}>
+
<CurrencySymbol boxSize={'10px'} mr={'2px'} />
<Text>{props.bouns}</Text>
</Flex>
</Flex>
</Flex>
) : (
<Flex
position={'absolute'}
minW={'max-content'}
left="78px"
top="4px"
px={'13.5px'}
py={'2.5px'}
color={'purple.600'}
background="purple.100"
alignItems={'center'}
borderRadius="10px 10px 10px 0px"
zIndex={'99'}
fontStyle="normal"
fontWeight="500"
fontSize="12px"
>
<Text mr="4px">{t('Bonus')}</Text>
<CurrencySymbol boxSize={'10px'} mr={'2px'} />
<Text> {props.bouns}</Text>
</Flex>
)}
<Flex align={'center'}>
<Currencysymbol boxSize="20px" type={currency} />
<Text ml="4px" fontStyle="normal" fontWeight="500" fontSize="24px">
Expand Down Expand Up @@ -283,33 +329,51 @@ const RechargeModal = forwardRef(
cancalPay();
_onClose();
};

const { session } = useSessionStore();
const { toast } = useCustomToast();
const { data: bonuses, isSuccess } = useQuery(
['bonus'],
['bonus', session.user.id],
() =>
request.get<
request.post<
any,
ApiResp<{
steps: number[];
ratios: number[];
specialDiscount: [number, number][];
discount: {
defaultSteps: Record<string, number>;
firstRechargeDiscount: Record<string, number>;
};
}>
>('/api/price/bonus'),
{}
);
const ratios = bonuses?.data?.ratios || [];
const steps = bonuses?.data?.steps || [];
const specialBonus = bonuses?.data?.specialDiscount;
const [defaultSteps, ratios, steps, specialBonus] = useMemo(() => {
const defaultSteps = Object.entries(bonuses?.data?.discount.defaultSteps || {}).toSorted(
(a, b) => +a[0] - +b[0]
);
const ratios = defaultSteps.map(([key, value]) => value);
const steps = defaultSteps.map(([key, value]) => +key);
const specialBonus = Object.entries(
bonuses?.data?.discount.firstRechargeDiscount || {}
).toSorted((a, b) => +a[0] - +b[0]);
const temp: number[] = [];
specialBonus.forEach(([k, v]) => {
const step = +k;
if (steps.findIndex((v) => step === v) === -1) {
temp.push(+k);
}
});
steps.unshift(...temp);
ratios.unshift(...temp.map(() => 0));
return [defaultSteps, ratios, steps, specialBonus];
}, [bonuses?.data?.discount.defaultSteps, bonuses?.data?.discount.firstRechargeDiscount]);
const [amount, setAmount] = useState(() => 0);
const getBonus = useCallback(
(amount: number) => {
if (isSuccess && ratios && steps && ratios.length === steps.length)
return getFavorable(steps, ratios, specialBonus)(amount);
else return 0;
},
[isSuccess, ratios, steps, specialBonus]
);
const getBonus = (amount: number) => {
let ratio = 0;
let specialIdx = specialBonus.findIndex(([k]) => +k === amount);
if (specialIdx >= 0) return Math.floor((amount * specialBonus[specialIdx][1]) / 100);
const step = [...steps].reverse().findIndex((step) => amount >= step);
if (ratios.length > step && step > -1) ratio = [...ratios].reverse()[step];
return Math.floor((amount * ratio) / 100);
};
const { stripeEnabled, wechatEnabled } = useEnvStore();
useEffect(() => {
if (steps && steps.length > 0) {
Expand Down Expand Up @@ -346,7 +410,6 @@ const RechargeModal = forwardRef(
<ModalHeader
px={'20px'}
py={'12px'}
pb={'18px'}
bg={'grayModern.25'}
borderBottom={'1px solid'}
fontWeight={500}
Expand Down Expand Up @@ -377,14 +440,31 @@ const RechargeModal = forwardRef(
</Text>
</Flex>
<Flex direction={'column'} mb={'20px'}>
<Text color="grayModern.600" fontWeight={'normal'} mb={'16px'}>
{t('Select Amount')}
</Text>
<Flex mb={'36px'} justify={'space-between'}>
<Text color="grayModern.600" fontWeight={'normal'}>
{t('Select Amount')}
</Text>
<Flex align={'center'}>
<GiftIcon boxSize={'16px'} mr={'8px'}></GiftIcon>
<Text
mr={'4px'}
color={'grayModern.900'}
fontSize={'14px'}
fontWeight={500}
>
{t('first_recharge_title')}!
</Text>
<MyTooltip label={<Text>{t('first_recharge_tips')}</Text>}>
<HelpIcon boxSize={'16px'}></HelpIcon>
</MyTooltip>
</Flex>
</Flex>
<Flex wrap={'wrap'} gap={'16px'}>
{steps.map((amount, index) => (
<BonusBox
key={index}
amount={amount}
isFirst={specialBonus.findIndex((a) => +a[0] === amount) >= 0}
bouns={getBonus(amount)}
onClick={() => {
setSelectAmount(index);
Expand Down Expand Up @@ -419,11 +499,13 @@ const RechargeModal = forwardRef(
variant={'unstyled'}
onChange={(str, v) => {
const maxAmount = 10_000_000;
if (!isNumber(v) || isNaN(v)) {
if (!str || !isNumber(v) || isNaN(v)) {
setAmount(0);
return;
}
if (v > maxAmount) {
setAmount(maxAmount);
return;
}
setAmount(v);
}}
Expand Down Expand Up @@ -506,12 +588,13 @@ const RechargeModal = forwardRef(
) : (
<>
<ModalHeader
m={'24px'}
p={'0'}
position={'absolute'}
display={'flex'}
alignItems={'center'}
height={'33px'}
px={'20px'}
py={'12px'}
bg={'grayModern.25'}
borderBottom={'1px solid'}
fontWeight={500}
fontSize={'16px'}
borderColor={'grayModern.100'}
>
<Img
src={vector.src}
Expand All @@ -526,9 +609,9 @@ const RechargeModal = forwardRef(
cancalPay();
}}
></Img>
<Text>{t('Recharge Amount')}</Text>
{t('Recharge Amount')}
</ModalHeader>
<ModalCloseButton top={'24px'} right={'24px'} />
<ModalCloseButton top={'8px'} right={'18px'} />
{payType === 'wechat' ? (
<WechatPayment
complete={complete}
Expand Down
36 changes: 36 additions & 0 deletions frontend/providers/costcenter/src/components/icons/GiftIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createIcon } from '@chakra-ui/react';

const GiftIcon = createIcon({
displayName: 'GiftIcon',
viewBox: '0 0 16 16',
path: (
<g>
<g clip-path="url(#clip0_5543_2151)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.99972 1.99935C4.7345 1.99935 4.48015 2.10471 4.29261 2.29224C4.10507 2.47978 3.99972 2.73413 3.99972 2.99935C3.99972 3.26457 4.10507 3.51892 4.29261 3.70646C4.48015 3.89399 4.7345 3.99935 4.99972 3.99935H7.1024C7.02101 3.78643 6.91799 3.55229 6.79057 3.32063C6.37126 2.55824 5.79565 1.99935 4.99972 1.99935ZM7.33305 5.33268V8.66602H1.99972V6.79935C1.99972 6.41498 2.00024 6.1668 2.01567 5.97791C2.03047 5.7968 2.05558 5.72965 2.07238 5.69669C2.13629 5.57125 2.23828 5.46926 2.36372 5.40534C2.39669 5.38855 2.46383 5.36343 2.64494 5.34863C2.83383 5.3332 3.08201 5.33268 3.46638 5.33268H7.33305ZM2.89326 4.00299C2.74529 3.6924 2.66638 3.34994 2.66638 2.99935C2.66638 2.38051 2.91222 1.78702 3.3498 1.34943C3.78739 0.911848 4.38088 0.666016 4.99972 0.666016C6.53712 0.666016 7.4615 1.77379 7.95886 2.67807C7.97273 2.70328 7.98634 2.72847 7.99972 2.75363C8.01309 2.72847 8.02671 2.70328 8.04057 2.67807C8.53793 1.77379 9.46232 0.666016 10.9997 0.666016C11.6186 0.666016 12.212 0.911848 12.6496 1.34943C13.0872 1.78702 13.333 2.38051 13.333 2.99935C13.333 3.34994 13.2541 3.6924 13.1062 4.00299C13.235 4.00581 13.3541 4.01083 13.4631 4.01973C13.7266 4.04126 13.9888 4.08881 14.241 4.21734C14.6174 4.40908 14.9233 4.71504 15.1151 5.09137C15.2436 5.34362 15.2911 5.60577 15.3127 5.86933C15.3331 6.11904 15.3331 6.42221 15.333 6.77364V12.5584C15.3331 12.9098 15.3331 13.213 15.3127 13.4627C15.2911 13.7263 15.2436 13.9884 15.1151 14.2407C14.9233 14.617 14.6174 14.9229 14.241 15.1147C13.9888 15.2432 13.7266 15.2908 13.4631 15.3123C13.2134 15.3327 12.9102 15.3327 12.5588 15.3327H8.00065C8.00034 15.3327 8.00003 15.3327 7.99972 15.3327C7.99941 15.3327 7.9991 15.3327 7.99879 15.3327H3.44067C3.08925 15.3327 2.78608 15.3327 2.53636 15.3123C2.2728 15.2908 2.01065 15.2432 1.7584 15.1147C1.38208 14.9229 1.07612 14.617 0.88437 14.2407C0.755841 13.9884 0.708297 13.7263 0.686763 13.4627C0.66636 13.213 0.66637 12.9098 0.666382 12.5584L0.666383 9.33306C0.666383 9.33294 0.666383 9.33319 0.666383 9.33306C0.666383 9.33294 0.666383 9.33243 0.666383 9.3323L0.666382 6.77365C0.666371 6.42222 0.66636 6.11905 0.686763 5.86933C0.708297 5.60577 0.755841 5.34362 0.88437 5.09137C1.07612 4.71504 1.38208 4.40908 1.7584 4.21734C2.01066 4.08881 2.2728 4.04126 2.53636 4.01973C2.6453 4.01083 2.76441 4.00581 2.89326 4.00299ZM1.99972 9.99935V12.5327C1.99972 12.917 2.00023 13.1652 2.01567 13.3541C2.03047 13.5352 2.05558 13.6024 2.07238 13.6353C2.13629 13.7608 2.23828 13.8628 2.36372 13.9267C2.39669 13.9435 2.46383 13.9686 2.64494 13.9834C2.83383 13.9988 3.08201 13.9993 3.46638 13.9993H7.33305V9.99935H1.99972ZM8.66638 9.99935V13.9993H12.5331C12.9174 13.9993 13.1656 13.9988 13.3545 13.9834C13.5356 13.9686 13.6027 13.9435 13.6357 13.9267C13.7612 13.8628 13.8631 13.7608 13.9271 13.6353C13.9439 13.6024 13.969 13.5352 13.9838 13.3541C13.9992 13.1652 13.9997 12.9171 13.9997 12.5327V9.99935H8.66638ZM13.9997 8.66602V6.79935C13.9997 6.41498 13.9992 6.1668 13.9838 5.97791C13.969 5.7968 13.9439 5.72965 13.9271 5.69669C13.8631 5.57125 13.7612 5.46926 13.6357 5.40535C13.6027 5.38855 13.5356 5.36343 13.3545 5.34864C13.1656 5.3332 12.9174 5.33268 12.5331 5.33268L8.66638 5.33268V8.66602H13.9997ZM8.89704 3.99935C8.97842 3.78643 9.08144 3.55229 9.20886 3.32063C9.62817 2.55824 10.2038 1.99935 10.9997 1.99935C11.2649 1.99935 11.5193 2.10471 11.7068 2.29224C11.8944 2.47978 11.9997 2.73413 11.9997 2.99935C11.9997 3.26457 11.8944 3.51892 11.7068 3.70646C11.5193 3.89399 11.2649 3.99935 10.9997 3.99935H8.89704Z"
fill="url(#paint0_linear_5543_2151)"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_5543_2151"
x1="7.99972"
y1="1.14221"
x2="7.99972"
y2="14.8565"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#69AEFF" />
<stop offset="0.432292" stop-color="#6096FF" />
<stop offset="1" stop-color="#6C7FFF" />
</linearGradient>
<clipPath id="clip0_5543_2151">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</g>
)
});
export default GiftIcon;
17 changes: 17 additions & 0 deletions frontend/providers/costcenter/src/components/icons/HelpIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createIcon } from '@chakra-ui/react';

export const HelpIcon = createIcon({
displayName: 'HelpIcon',
viewBox: '0 0 16 16',
path: (
<g fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M8.00005 2.55678C4.99385 2.55678 2.55684 4.99379 2.55684 7.99998C2.55684 11.0062 4.99385 13.4432 8.00005 13.4432C11.0062 13.4432 13.4432 11.0062 13.4432 7.99998C13.4432 4.99379 11.0062 2.55678 8.00005 2.55678ZM1.22351 7.99998C1.22351 4.25741 4.25747 1.22345 8.00005 1.22345C11.7426 1.22345 14.7766 4.25741 14.7766 7.99998C14.7766 11.7426 11.7426 14.7765 8.00005 14.7765C4.25747 14.7765 1.22351 11.7426 1.22351 7.99998ZM8.14834 5.62577C7.87687 5.57921 7.59769 5.63022 7.36023 5.76978C7.12277 5.90934 6.94236 6.12843 6.85096 6.38825C6.72878 6.73558 6.34817 6.9181 6.00085 6.79591C5.65352 6.67373 5.471 6.29312 5.59318 5.9458C5.78908 5.38894 6.17572 4.91937 6.68465 4.62027C7.19358 4.32117 7.79194 4.21184 8.37375 4.31163C8.95556 4.41143 9.48328 4.71392 9.86345 5.16552C10.2435 5.61702 10.4516 6.18845 10.4508 6.77862C10.4505 7.74318 9.73585 8.37516 9.23745 8.70743C8.96791 8.88713 8.70311 9.01905 8.50833 9.10562C8.40999 9.14933 8.32711 9.18252 8.26726 9.20532C8.23728 9.21674 8.21291 9.22562 8.19509 9.23195L8.17333 9.23957L8.16632 9.24197L8.16381 9.24282L8.16281 9.24315C8.16261 9.24322 8.16199 9.24343 7.95117 8.61097L8.16199 9.24343C7.81269 9.35986 7.43514 9.17109 7.31871 8.82179C7.20232 8.47261 7.39093 8.0952 7.74 7.97863L7.74841 7.97567C7.75744 7.97246 7.77246 7.96701 7.7926 7.95934C7.83298 7.94395 7.89331 7.91987 7.96681 7.8872C8.1157 7.82103 8.30915 7.72383 8.49785 7.59803C8.91582 7.31938 9.11746 7.03509 9.11746 6.77801L9.11746 6.77702C9.11787 6.50159 9.02079 6.2349 8.84341 6.02419C8.66603 5.81347 8.41981 5.67234 8.14834 5.62577ZM7.33338 11.0549C7.33338 10.6867 7.63186 10.3883 8.00005 10.3883H8.00616C8.37435 10.3883 8.67282 10.6867 8.67282 11.0549C8.67282 11.4231 8.37435 11.7216 8.00616 11.7216H8.00005C7.63186 11.7216 7.33338 11.4231 7.33338 11.0549Z"
fill="#667085"
/>
</g>
)
});
export default HelpIcon;
Loading

0 comments on commit 54a3bc2

Please sign in to comment.