* perf(costcenter):stripe tips

* feat(costcenter):support switch currency symbol

* applaunchpad

* applaunchpad

* i18n
This commit is contained in:
xudaotutou
2023-08-14 15:26:51 +08:00
committed by GitHub
parent 72e5d94f40
commit c2df6ef12a
40 changed files with 328 additions and 231 deletions
@@ -78,3 +78,8 @@ export const appDeployKey = 'cloud.sealos.io/app-deploy-manager';
export const domainKey = `cloud.sealos.io/app-deploy-manager-domain`;
export const gpuNodeSelectorKey = 'nvidia.com/gpu.product';
export const gpuResourceKey = 'nvidia.com/gpu';
export enum Coin {
cny = 'cny',
shellCoin = 'shellCoin',
usd = 'usd'
}
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/services/backend/response';
import type { FormSliderListType } from '@/types';
import { readFileSync } from 'fs';
import { noGpuSliderKey } from '@/constants/app';
import { Coin, noGpuSliderKey } from '@/constants/app';
export type Response = {
SEALOS_DOMAIN: string;
@@ -10,6 +10,7 @@ export type Response = {
INGRESS_SECRET: string;
SHOW_EVENT_ANALYZE: boolean;
FORM_SLIDER_LIST_CONFIG: FormSliderListType;
CURRENCY: Coin;
};
export const defaultVal = {
@@ -37,14 +38,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
global.FormSliderListConfig = defaultVal;
}
jsonRes<Response>(res, {
data: {
SEALOS_DOMAIN: process.env.SEALOS_DOMAIN || 'cloud.sealos.io',
DOMAIN_PORT: process.env.DOMAIN_PORT || '',
INGRESS_SECRET: process.env.INGRESS_SECRET || 'wildcard-cert',
SHOW_EVENT_ANALYZE: !!process.env.FASTGPT_KEY,
FORM_SLIDER_LIST_CONFIG: global.FormSliderListConfig
FORM_SLIDER_LIST_CONFIG: global.FormSliderListConfig,
CURRENCY: (process.env['CURRENCY'] || Coin.shellCoin) as Coin
}
});
}
@@ -2,7 +2,29 @@ import React, { useMemo } from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { useGlobalStore } from '@/store/global';
import { useTranslation } from 'next-i18next';
import { Text, Icon } from '@chakra-ui/react';
import { CURRENCY } from '@/store/static';
function Currencysymbol({
type = 'shellCoin',
...props
}: {
type?: 'shellCoin' | 'cny' | 'usd';
} & Pick<Parameters<typeof Icon>[0], 'w' | 'h' | 'color'>) {
return type === 'shellCoin' ? (
<Icon h="auto" {...props} viewBox="0 0 43 43">
<path
fill="currentColor"
fillRule="evenodd"
clip-rule="evenodd"
d="M38.8452 16.3383C38.8452 16.3383 38.169 13.0791 34.6177 12.5018C34.6177 12.5018 32.91 5.22138 26.476 8.01336C26.476 8.01336 21.7775 1.34481 16.7833 8.01336C16.7833 8.01336 10.8267 4.86984 8.35511 12.513C8.35511 12.513 4.03522 12.6233 4.00011 18.5272C3.96499 24.4311 12.4728 30.8168 13.6867 31.3741C13.6867 31.3741 11.0158 34.2719 14.2876 36.284C18.8172 38.6577 27.3756 37.7936 29.4606 35.9326C30.7532 34.7789 30.6523 33.0064 29.4606 31.5498C29.4556 31.5548 40.4202 25.1038 38.8452 16.3383ZM31.6228 11.4841C29.9736 8.80397 26.7453 10.5751 26.7453 10.5751C27.9505 20.3212 24.1256 29.8479 24.1256 29.8479C27.7512 28.7132 33.6121 14.7169 31.6228 11.4841ZM21.1951 7.66792C21.1951 7.66792 18.5641 7.83387 18.1203 11.4413C17.6764 15.0488 18.7299 29.0557 21.5427 30.0673C21.5427 30.0673 23.5266 30.5865 24.8902 18.319C26.6924 6.40218 21.1951 7.66792 21.1951 7.66792ZM16.2936 10.5751C16.2936 10.5751 13.488 8.61467 11.4786 11.4841C9.49465 14.7169 15.3556 28.7132 18.9759 29.8479C18.9759 29.8479 15.2143 21.3388 16.2936 10.5751ZM8.87726 14.9454C8.87726 14.9454 6.13705 14.9454 6.43049 18.7633C6.79949 23.5643 12.9309 28.6588 15.2143 29.5044C15.2089 29.5044 10.0763 22.326 8.87726 14.9454ZM15.1097 34.6383C15.8744 35.9813 24.6742 36.3866 28.102 34.8719C28.102 34.8719 29.1783 33.7787 27.532 31.5994C27.4731 31.5216 26.8769 31.5791 26.5874 31.5627C26.2978 31.5463 26.0133 31.4758 25.6763 31.4068C25.6763 31.4068 24.6168 32.1662 22.9437 31.5994C22.9437 31.5994 21.7614 32.2601 20.3823 31.5994C20.3823 31.5994 18.6756 32.1821 17.6497 31.4068C17.6497 31.4068 16.9291 31.6032 15.8719 31.4068C15.8719 31.4068 14.3671 33.3342 15.1097 34.6383ZM34.2051 14.9533C32.8914 23.1395 27.9505 29.5113 27.9505 29.5113C30.9382 28.8784 36.0244 22.5811 36.6816 18.7633C37.3388 14.9454 34.2051 14.9533 34.2051 14.9533Z"
/>
</Icon>
) : type === 'cny' ? (
<Text {...props}></Text>
) : (
<Text {...props}>$</Text>
);
}
const PriceBox = ({
cpu,
memory,
@@ -40,7 +62,12 @@ const PriceBox = ({
const podScale = (val: number) => {
const min = (val * pods[0]).toFixed(2);
const max = (val * pods[1]).toFixed(2);
return pods[0] === pods[1] ? `${min}` : `${min} ~ ${max}`;
return (
<Flex>
<Currencysymbol type={CURRENCY} />
<Text ml="4px">{pods[0] === pods[1] ? `${min}` : `${min} ~ ${max}`}</Text>
</Flex>
);
};
return [
@@ -1,10 +1,11 @@
import { getInitData } from '@/api/platform';
import { Coin } from '@/constants/app';
export let SEALOS_DOMAIN = 'cloud.sealos.io';
export let DOMAIN_PORT = '';
export let INGRESS_SECRET = 'wildcard-cert';
export let SHOW_EVENT_ANALYZE = false;
export let CURRENCY = Coin.shellCoin;
export const loadInitData = async () => {
try {
const res = await getInitData();
@@ -12,11 +13,13 @@ export const loadInitData = async () => {
DOMAIN_PORT = res.DOMAIN_PORT;
INGRESS_SECRET = res.INGRESS_SECRET;
SHOW_EVENT_ANALYZE = res.SHOW_EVENT_ANALYZE;
CURRENCY = res.CURRENCY;
return {
SEALOS_DOMAIN,
DOMAIN_PORT,
INGRESS_SECRET,
CURRENCY,
FORM_SLIDER_LIST_CONFIG: res.FORM_SLIDER_LIST_CONFIG
};
} catch (error) {}
+4
View File
@@ -1,5 +1,7 @@
# for open transfer
# TRANSFER_ENABLED="true"
# WECHAT_ENABLED="true"
# STRIPE_ENABLED="true"
# for open transfer
# RECHARGE_ENABLED="true"
@@ -7,8 +9,10 @@
# for open GPU measure
# GPU_ENABLED="true"
# for Stripe pay
# STRIPE_PUB
# for open invoice
# INVOICE_ENABLED="true"
# MONGODB_URI
@@ -66,6 +66,8 @@
"he payee cannot be oneself": "The payee cannot be oneself",
"True Amount": "True Amount",
"Get Code": "verification",
"Stripe Success": "pay with Stripe successfully",
"Stripe Cancel": "cancel to pay with Stripe",
"orders": {
"list": "Order List",
"invoiceAmount": "Invoice Amount",
@@ -66,6 +66,8 @@
"Transfer Success": "转账成功",
"True Amount": "实际金额",
"Get Code": "获取验证码",
"Stripe Success": "Stripe 支付完成",
"Stripe Cancel": "Stripe 支付取消",
"orders": {
"list": "订单列表",
"invoiceAmount": "发票金额",
@@ -0,0 +1,22 @@
import { Text, Icon } from '@chakra-ui/react';
export default function currencysymbol({
type = 'shellCoin',
...props
}: {
type?: 'shellCoin' | 'cny' | 'usd';
} & Pick<Parameters<typeof Icon>[0], 'w' | 'h' | 'color'>) {
return type === 'shellCoin' ? (
<Icon h="auto" {...props} viewBox="0 0 43 43">
<path
fill="currentColor"
fillRule="evenodd"
clip-rule="evenodd"
d="M38.8452 16.3383C38.8452 16.3383 38.169 13.0791 34.6177 12.5018C34.6177 12.5018 32.91 5.22138 26.476 8.01336C26.476 8.01336 21.7775 1.34481 16.7833 8.01336C16.7833 8.01336 10.8267 4.86984 8.35511 12.513C8.35511 12.513 4.03522 12.6233 4.00011 18.5272C3.96499 24.4311 12.4728 30.8168 13.6867 31.3741C13.6867 31.3741 11.0158 34.2719 14.2876 36.284C18.8172 38.6577 27.3756 37.7936 29.4606 35.9326C30.7532 34.7789 30.6523 33.0064 29.4606 31.5498C29.4556 31.5548 40.4202 25.1038 38.8452 16.3383ZM31.6228 11.4841C29.9736 8.80397 26.7453 10.5751 26.7453 10.5751C27.9505 20.3212 24.1256 29.8479 24.1256 29.8479C27.7512 28.7132 33.6121 14.7169 31.6228 11.4841ZM21.1951 7.66792C21.1951 7.66792 18.5641 7.83387 18.1203 11.4413C17.6764 15.0488 18.7299 29.0557 21.5427 30.0673C21.5427 30.0673 23.5266 30.5865 24.8902 18.319C26.6924 6.40218 21.1951 7.66792 21.1951 7.66792ZM16.2936 10.5751C16.2936 10.5751 13.488 8.61467 11.4786 11.4841C9.49465 14.7169 15.3556 28.7132 18.9759 29.8479C18.9759 29.8479 15.2143 21.3388 16.2936 10.5751ZM8.87726 14.9454C8.87726 14.9454 6.13705 14.9454 6.43049 18.7633C6.79949 23.5643 12.9309 28.6588 15.2143 29.5044C15.2089 29.5044 10.0763 22.326 8.87726 14.9454ZM15.1097 34.6383C15.8744 35.9813 24.6742 36.3866 28.102 34.8719C28.102 34.8719 29.1783 33.7787 27.532 31.5994C27.4731 31.5216 26.8769 31.5791 26.5874 31.5627C26.2978 31.5463 26.0133 31.4758 25.6763 31.4068C25.6763 31.4068 24.6168 32.1662 22.9437 31.5994C22.9437 31.5994 21.7614 32.2601 20.3823 31.5994C20.3823 31.5994 18.6756 32.1821 17.6497 31.4068C17.6497 31.4068 16.9291 31.6032 15.8719 31.4068C15.8719 31.4068 14.3671 33.3342 15.1097 34.6383ZM34.2051 14.9533C32.8914 23.1395 27.9505 29.5113 27.9505 29.5113C30.9382 28.8784 36.0244 22.5811 36.6816 18.7633C37.3388 14.9454 34.2051 14.9533 34.2051 14.9533Z"
/>
</Icon>
) : type === 'cny' ? (
<Text {...props}></Text>
) : (
<Text {...props}>$</Text>
);
}
@@ -5,12 +5,13 @@ import lineUp from '@/assert/lineUp.svg';
import { Flex, Img, Table, TableContainer, Tbody, Td, Text, Th, Thead, Tr } from '@chakra-ui/react';
import { format, parseISO } from 'date-fns';
import { formatMoney } from '@/utils/format';
import shellCoin from '@/assert/shell_coin.svg';
import { useTranslation } from 'next-i18next';
import useEnvStore from '@/stores/env';
import CurrencySymbol from '../CurrencySymbol';
export function BillingTable({ data }: { data: BillingItem[] }) {
const { t } = useTranslation();
const gpuEnabled = useEnvStore((state) => state.gpuEnabled);
const currency = useEnvStore((s) => s.currency);
return (
<TableContainer w="100%" mt="0px">
<Table variant="simple">
@@ -28,7 +29,12 @@ export function BillingTable({ data }: { data: BillingItem[] }) {
background: '#F1F4F6'
}}
>
{t(item)}
<Flex display={'flex'} alignItems={'center'}>
<Text mr="4px">{t(item)}</Text>
{['CPU', 'Gpu', 'Memory', 'Storage', 'Total Amount'].includes(item) && (
<CurrencySymbol type={currency} />
)}
</Flex>
</Th>
))}
</Tr>
@@ -80,57 +86,13 @@ export function BillingTable({ data }: { data: BillingItem[] }) {
</Flex>
</Flex>
</Td>
<Td>
{!item.type ? (
<Flex>
<Img src={shellCoin.src} mr="4px" />
<span>{formatMoney(item.costs?.cpu || 0)}</span>
</Flex>
) : (
'-'
)}
</Td>
<Td>
{!item.type ? (
<Flex>
<Img src={shellCoin.src} mr="4px" />
<span>{formatMoney(item.costs?.memory || 0)}</span>{' '}
</Flex>
) : (
'-'
)}
</Td>
<Td>
{!item.type ? (
<Flex>
<Img src={shellCoin.src} mr="4px" />
<span>{formatMoney(item.costs?.storage || 0)}</span>{' '}
</Flex>
) : (
'-'
)}
</Td>
<Td>{!item.type ? <span>{formatMoney(item.costs?.cpu || 0)}</span> : '-'}</Td>
<Td>{!item.type ? <span>{formatMoney(item.costs?.memory || 0)}</span> : '-'}</Td>
<Td>{!item.type ? <span>{formatMoney(item.costs?.storage || 0)}</span> : '-'}</Td>
{gpuEnabled && (
<Td>
{!item.type ? (
<Flex>
<Img src={shellCoin.src} mr="4px" />
<span>{formatMoney(item.costs?.gpu || 0)}</span>{' '}
</Flex>
) : (
'-'
)}
</Td>
<Td>{!item.type ? <span>{formatMoney(item.costs?.gpu || 0)}</span> : '-'}</Td>
)}
<Td>
{
<Flex>
<Img src={shellCoin.src} mr="4px" />
<span>{formatMoney(item.amount)}</span>
</Flex>
}
</Td>
<Td>{<span>{formatMoney(item.amount)}</span>}</Td>
</Tr>
);
})}
@@ -6,10 +6,12 @@ import { useMemo } from 'react';
import { formatMoney } from '@/utils/format';
import { useTranslation } from 'next-i18next';
import useBillingData from '@/hooks/useBillingData';
import shellcoin from '@/assert/shell_coin.svg';
import CurrencySymbol from '../CurrencySymbol';
import useEnvStore from '@/stores/env';
export function Buget() {
const { t } = useTranslation();
const { data } = useBillingData();
const currency = useEnvStore((s) => s.currency);
const [_out, _in] = useMemo(
() =>
(data?.data?.status.item || []).reduce<[number, number]>(
@@ -52,8 +54,8 @@ export function Buget() {
{t(v.title)}
</Text>
<Flex mt="8px">
<Img src={shellcoin.src} mr="4px" />
<Text fontWeight="500" fontSize="16px">
<CurrencySymbol w="16px" type={currency} />
<Text fontWeight="500" fontSize="16px" ml="4px">
{v.value}
</Text>
</Flex>
@@ -1,4 +1,3 @@
import shellcoin from '@/assert/shell_coin.svg';
import {
Box,
Button,
@@ -33,6 +32,8 @@ import stripe_icon from '@/assert/bi_stripe.svg';
import { Spinner } from '@chakra-ui/react';
import { Stripe } from '@stripe/stripe-js';
import type { AxiosInstance } from 'axios';
import useEnvStore from '@/stores/env';
import Currencysymbol from '@/components/CurrencySymbol';
const StripeForm = (props: {
tradeNO?: string;
complete: number;
@@ -123,6 +124,7 @@ const BonusBox = (props: {
amount: number;
}) => {
const { t } = useTranslation();
const currency = useEnvStore((s) => s.currency);
return (
<Flex
width="140px"
@@ -165,8 +167,8 @@ const BonusBox = (props: {
{t('Bonus')} {props.bouns}
</Text>
<Flex align={'center'}>
<Img src={shellcoin.src} w="24px" h="24px" mr="4px" />
<Text fontStyle="normal" fontWeight="500" fontSize="24px">
<Currencysymbol w="24px" type={currency} />
<Text ml="4px" fontStyle="normal" fontWeight="500" fontSize="24px">
{props.amount}
</Text>
</Flex>
@@ -308,7 +310,7 @@ const RechargeModal = forwardRef(
},
[isSuccess, ratios, steps]
);
const { stripeEnabled, wechatEnabled } = useEnvStore();
useEffect(() => {
if (steps && steps.length > 0) {
setAmount(steps[0]);
@@ -322,10 +324,11 @@ const RechargeModal = forwardRef(
};
const handleStripeConfirm = () => {
setComplete(1);
setPayType('stripe');
setComplete(1);
createPaymentRes.mutate();
};
const currency = useEnvStore((s) => s.currency);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
@@ -352,8 +355,8 @@ const RechargeModal = forwardRef(
<Text color="#7B838B" fontWeight={'normal'} mr={'20px'}>
{t('Balance')}
</Text>
<Img src={shellcoin.src} w="24px" h="24px" mr="4px" />
<Text color="#24282C" fontSize="24px" fontWeight={'medium'}>
<Currencysymbol w="24px" type={currency} />
<Text ml="4px" color="#24282C" fontSize="24px" fontWeight={'medium'}>
{formatMoney(balance).toFixed(2)}
</Text>
</Flex>
@@ -399,7 +402,7 @@ const RechargeModal = forwardRef(
variant={'unstyled'}
onChange={(str, v) => (str.trim() ? setAmount(v) : setAmount(0))}
>
<Img src={shellcoin.src} w="16px" h="16px" mr="4px" />
<Currencysymbol w="16px" type={currency} />
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper>
@@ -434,34 +437,38 @@ const RechargeModal = forwardRef(
<OuterLink text={t('View Discount Rules')}></OuterLink>
</Flex>
<Flex gap={'16px'} width={'full'}>
<Button
size="primary"
variant="primary"
mt="20px"
w="full"
h="auto"
py="14px"
px="34px"
onClick={() => {
handleStripeConfirm();
}}
>
<Img src={stripe_icon.src} mr="2px" w="24px" h="24px" />
<Text>{t('pay with stripe')}</Text>
</Button>
<Button
size="primary"
variant="primary"
mt="20px"
w="full"
h="auto"
py="14px"
px="34px"
onClick={() => handleWechatConfirm()}
>
<Img src={wechat_icon.src} mr="2px" w="24px" h="24px" />
<Text>{t('pay with wechat')}</Text>
</Button>
{stripeEnabled && (
<Button
size="primary"
variant="primary"
mt="20px"
w="full"
h="auto"
py="14px"
px="34px"
onClick={() => {
handleStripeConfirm();
}}
>
<Img src={stripe_icon.src} mr="2px" w="24px" h="24px" />
<Text>{t('pay with stripe')}</Text>
</Button>
)}
{wechatEnabled && (
<Button
size="primary"
variant="primary"
mt="20px"
w="full"
h="auto"
py="14px"
px="34px"
onClick={() => handleWechatConfirm()}
>
<Img src={wechat_icon.src} mr="2px" w="24px" h="24px" />
<Text>{t('pay with wechat')}</Text>
</Button>
)}
</Flex>
</Flex>
</>
@@ -1,5 +1,5 @@
import request from '@/service/request';
import shellcoin from '@/assert/shell_coin.svg';
import Currencysymbol from '@/components/CurrencySymbol';
import {
Button,
Flex,
@@ -15,7 +15,6 @@ import {
NumberInput,
NumberInputField,
NumberInputStepper,
Spinner,
Text,
useDisclosure,
useToast
@@ -27,6 +26,7 @@ import { deFormatMoney, formatMoney } from '@/utils/format';
import { useTranslation } from 'next-i18next';
import { ApiResp } from '@/types/api';
import { TransferState, transferStatus } from '@/types/Transfer';
import useEnvStore from '@/stores/env';
const TransferModal = forwardRef(
(
@@ -51,6 +51,7 @@ const TransferModal = forwardRef(
}),
[]
);
const currency = useEnvStore((s) => s.currency);
const { t } = useTranslation();
const { isOpen, onOpen, onClose: _onClose } = useDisclosure();
const [to, setTo] = useState('');
@@ -216,8 +217,8 @@ const TransferModal = forwardRef(
onChange={(str, v) => (str.trim() ? setAmount(v) : setAmount(0))}
isDisabled={mutation.isLoading}
>
<Img mr={'4px'} src={shellcoin.src} w="16px" h="16px" />
<NumberInputField />
<Currencysymbol w="16px" type={currency} />
<NumberInputField ml="4px" />
<NumberInputStepper>
<NumberIncrementStepper>
<Img src={vector.src}></Img>
@@ -231,8 +232,8 @@ const TransferModal = forwardRef(
<Text fontSize="12px" mr={'12px'}>
{t('Balance')}
</Text>
<Img mr={'4px'} src={shellcoin.src} w="16px" h="16px" />
<Text color="rgba(33, 155, 244, 1)" fontSize={'16px'}>
<Currencysymbol w="16px" type={currency} color="rgba(33, 155, 244, 1)" />
<Text color="rgba(33, 155, 244, 1)" fontSize={'16px'} ml="4px">
{formatMoney(balance).toFixed(2)}
</Text>
<Button
@@ -13,7 +13,7 @@ import jsyaml from 'js-yaml';
import RechargeModal from './RechargeModal';
import TransferModal from './TransferModal';
import useEnvStore from '@/stores/env';
import shellCoin from '@/assert/shell_coin.svg';
import CurrencySymbol from '@/components/CurrencySymbol';
export default memo(function UserCard() {
const getSession = useSessionStore((state) => state.getSession);
const transferEnabled = useEnvStore((state) => state.transferEnabled);
@@ -44,6 +44,7 @@ export default memo(function UserCard() {
if (balance_raw?.data?.deductionBalance) {
real_balance -= balance_raw?.data.deductionBalance;
}
const currency = useEnvStore((s) => s.currency);
const balance = real_balance;
const stripePromise = useEnvStore((s) => s.stripePromise);
return (
@@ -51,7 +52,6 @@ export default memo(function UserCard() {
<Flex
className={styles.userCard}
boxShadow={'0 4px #BCBFC3,0 8px #DFE2E6'}
// aspectRatio={'2/1'}
pt="13px"
pb={'19px'}
mb={'34px'}
@@ -76,8 +76,8 @@ export default memo(function UserCard() {
{t('Balance')}
</Box>
<Flex fontSize="24px" fontWeight="500" alignSelf={'center'} mt="3px !important">
<Img src={shellCoin.src} w="16px" h="16px" mixBlendMode={'exclusion'} />
<Text>{displayMoney(formatMoney(balance))}</Text>
<CurrencySymbol color={'white'} w="20px" type={currency} />
<Text ml="6px">{displayMoney(formatMoney(balance))}</Text>
</Flex>
<Flex alignItems="center" alignSelf={'center'} gap="10px" mt={'20px !important'}>
{transferEnabled && (
@@ -3,6 +3,8 @@ import { Checkbox, Flex, Table, TableContainer, Tbody, Td, Th, Thead, Tr } from
import { format } from 'date-fns';
import { useTranslation } from 'next-i18next';
import { ReqGenInvoice } from '@/types';
import CurrencySymbol from '../CurrencySymbol';
import useEnvStore from '@/stores/env';
export function InvoiceTable({
data,
@@ -15,6 +17,7 @@ export function InvoiceTable({
}) {
const { t } = useTranslation();
const needSelect = !!onSelect;
const currency = useEnvStore((s) => s.currency);
return (
<TableContainer w="100%" mt="0px" border={'1px solid #DEE0E2'} borderRadius="2px">
<Table variant="simple">
@@ -32,7 +35,10 @@ export function InvoiceTable({
background: '#F1F4F6'
}}
>
{t(item)}
<Flex display={'flex'}>
{t(item)}
{item !== 'Order Number' && <CurrencySymbol type={currency} />}
</Flex>
</Th>
))}
</Tr>
@@ -57,7 +63,7 @@ export function InvoiceTable({
</Flex>
</Td>
<Td>{format(item.createdTime, 'MM-dd HH:mm')}</Td>
<Td color={'#219BF4'}>{'¥' + item.amount}</Td>
<Td color={'#219BF4'}>{item.amount}</Td>
</Tr>
))}
</Tbody>
@@ -1,14 +1,15 @@
import useBillingStore from '@/stores/billing';
import useEnvStore from '@/stores/env';
import { displayMoney } from '@/utils/format';
import { Flex, Img } from '@chakra-ui/react';
import { Flex, Img, Text } from '@chakra-ui/react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import shellcoin from '@/assert/shell_coin.svg';
import CurrencySymbol from '../CurrencySymbol';
export default function PredictCard() {
const { t } = useTranslation();
const total = t('total');
const state = useBillingStore();
const currency = useEnvStore((s) => s.currency);
const gpuEnabled = useEnvStore((state) => state.gpuEnabled);
const leastCost = useMemo(() => {
const origin = [
@@ -45,8 +46,8 @@ export default function PredictCard() {
{item.name}
</Flex>
<Flex justify={'center'} align={'center'} h={'68px'}>
<Img src={shellcoin.src} h="16px" w="16px" mr="4px" />
{item.cost}
<CurrencySymbol w="14px" type={currency} />
<Text ml="4px">{item.cost}</Text>
</Flex>
</Flex>
))}
@@ -72,11 +72,15 @@ const App = ({ Component, pageProps }: AppProps) => {
(async () => {
try {
const { data } = await request<any, ApiResp<EnvData>>('/api/enabled');
state.setInvoiceEnabled(!!data?.invoiceEnabled);
state.setTransferEnabled(!!data?.transferEnabled);
state.setRechargeEnabled(!!data?.rechargeEnabled);
state.setGpuEnabled(!!data?.gpuEnabled);
state.setStripe(data?.stripePub || '');
state.setEnv('invoiceEnabled', !!data?.invoiceEnabled);
state.setEnv('transferEnabled', !!data?.transferEnabled);
state.setEnv('rechargeEnabled', !!data?.rechargeEnabled);
state.setEnv('currency', data?.currency || 'shellCoin');
state.setEnv('gpuEnabled', !!data?.gpuEnabled);
const stripeE = !!data?.stripeEnabled;
state.setEnv('stripeEnabled', stripeE);
stripeE && state.setStripe(data?.stripePub || '');
state.setEnv('wechatEnabled', !!data?.wechatEnabled);
} catch (error) {
console.error('get env error');
}
@@ -1,4 +1,11 @@
import { enabelGpu, enableInvoice, enableRecharge, enableTransfer } from '@/service/enabled';
import {
enableGpu,
enableInvoice,
enableRecharge,
enableStripe,
enableTransfer,
enableWechat
} from '@/service/enabled';
import { jsonRes } from '@/service/backend/response';
import { NextApiRequest, NextApiResponse } from 'next';
import { EnvData } from '@/types/env';
@@ -10,8 +17,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
rechargeEnabled: enableRecharge(),
transferEnabled: enableTransfer(),
invoiceEnabled: enableInvoice(),
gpuEnabled: enabelGpu(),
stripePub: process.env['STRIPE_PUB'] || ''
gpuEnabled: enableGpu(),
stripePub: process.env['STRIPE_PUB'] || '',
stripeEnabled: enableStripe(),
wechatEnabled: enableWechat(),
currency: (process.env['CURRENCY'] as EnvData['currency']) || 'shellCoin'
},
code: 200
});
@@ -1,17 +0,0 @@
.container {
table thead tr {
background: #f8f8fa;
}
td,
th {
border: none;
text-align: center;
&:first-child {
text-align: left;
}
&:last-child {
text-align: right;
}
}
}
@@ -2,7 +2,7 @@ import bar_icon from '@/assert/bar_chart_4_bars_black.svg';
import { BillingTable } from '@/components/billing/billingTable';
import SelectRange from '@/components/billing/selectDateRange';
import useNotEnough from '@/hooks/useNotEnough';
import { Box, Flex, Heading, Img } from '@chakra-ui/react';
import { Box, Flex, Heading, Img, useToast } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useEffect, useMemo } from 'react';
@@ -17,6 +17,7 @@ import { QueryClient } from '@tanstack/react-query';
import request from '@/service/request';
import useBillingStore from '@/stores/billing';
import { isSameDay, isSameHour, parseISO } from 'date-fns';
import { useRouter } from 'next/router';
function CostOverview() {
const { t, i18n } = useTranslation();
const updateCPU = useBillingStore((state) => state.updateCpu);
@@ -35,6 +36,29 @@ function CostOverview() {
() => data?.data?.status.item.filter((v) => v.type === 0) || [],
[data]
);
const totast = useToast();
const router = useRouter();
const { stripeState } = router.query;
useEffect(() => {
if (stripeState === 'success') {
totast({
status: 'success',
duration: 3000,
title: t('Stripe Success'),
isClosable: true,
position: 'top'
});
} else if (stripeState === 'error') {
totast({
status: 'error',
duration: 3000,
title: t('Stripe Cancel'),
isClosable: true,
position: 'top'
});
}
!!stripeState && router.replace(router.pathname);
}, [stripeState]);
useEffect(() => {
if (costBillingItems.length === 0) return;
const time = parseISO(costBillingItems[0].time);
@@ -1,4 +1,3 @@
// import { MockBillingData } from '@/mock/billing';
import { InvoiceTable } from '@/components/invoice/invoiceTable';
import { Box, Button, Flex, Heading, Img, Input, Text } from '@chakra-ui/react';
import { useEffect, useRef, useState } from 'react';
@@ -1,12 +1,11 @@
import { useRouter } from 'next/router';
import { useEffect } from 'react';
export default function Index() {
const router = useRouter();
useEffect(() => {
router.push('/cost_overview');
}, [router]);
router.push({
pathname: '/cost_overview',
query: router.query
});
return <div></div>;
}
@@ -15,7 +15,7 @@ import OuterLink from '@/components/outerLink';
import NotFound from '@/components/notFound';
import PredictCard from '@/components/valuation/predictCard';
import useEnvStore from '@/stores/env';
import shellcoin from '@/assert/shell_coin.svg';
import CurrencySymbol from '@/components/CurrencySymbol';
type CardItem = {
title: string;
price: number[];
@@ -46,6 +46,7 @@ function Valuation() {
const { t, i18n } = useTranslation();
const cookie = getCookie('NEXT_LOCALE');
const gpuEnabled = useEnvStore((state) => state.gpuEnabled);
const currency = useEnvStore((s) => s.currency);
useEffect(() => {
i18n.changeLanguage(cookie);
}, [cookie, i18n]);
@@ -111,12 +112,14 @@ function Valuation() {
justifyContent="center"
alignContent={'center'}
>
<Img src={shellcoin.src} h="28px" w="28px" my="auto" mr="4px" />
<Text>{item.price[0]}</Text>
</Heading>
<Text ml="4px">
{item.unit} / {t('Hour')}
</Text>
<Flex align={'center'}>
<CurrencySymbol w="16px" type={currency} />
<Text ml="4px">
{item.unit} / {t('Hour')}
</Text>
</Flex>
<Box pt={'17px'}>
{CYCLE.map((_item, idx) => (
<Flex
@@ -128,8 +131,8 @@ function Valuation() {
>
<Box>{item.price[idx + 1]}</Box>
<Flex align={'center'}>
<Img src={shellcoin.src} h="16px" w="16px" mr={'4px'} />
<Text>{`${item.unit} / ${t(_item)}`}</Text>
<CurrencySymbol w="16px" h="16px" type={currency} />
<Text ml="4px">{`${item.unit} / ${t(_item)}`}</Text>
</Flex>
</Flex>
))}
@@ -154,17 +157,14 @@ function Valuation() {
pt="12px"
px="24px"
>
<Flex>
<Img src={shellcoin.src} h="16px" w="16px" />
<Text>{`${item.price}`}</Text>
</Flex>
<Text>{`${item.price}`}</Text>
<Stack align={'flex-end'} gap="0" fontSize={'10px'} fontWeight={'500'}>
<Flex>
<Img src={nvidaIcon.src} w="14px" h="14px" mr="6px" />
<Text minW={'max-content'}>{item.name}</Text>
</Flex>
<Flex>
<Img src={shellcoin.src} h="16px" w="16px" />
<CurrencySymbol type={currency} />
<Text>{`${gpuProps.unit} / ${t('Hour')}`}</Text>
</Flex>
</Stack>
@@ -8,4 +8,7 @@ export const enableInvoice = () =>
!!process.env['ALI_SIGN_NAME'] &&
!!process.env['ALI_TEMPLATE_CODE'] &&
!!process.env['MONGODB_URI'];
export const enabelGpu = () => !!process.env['GPU_ENABLED'];
export const enableGpu = () => !!process.env['GPU_ENABLED'];
export const enableWechat = () => process.env['WECHAT_ENABLED'] === 'true';
export const enableStripe = () =>
process.env['STRIPE_ENABLED'] === 'true' && !!process.env['STRIPE_PUB'];
+13 -12
View File
@@ -1,30 +1,31 @@
import { Stripe, loadStripe } from '@stripe/stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import { create } from 'zustand';
type EnvState = {
rechargeEnabled: boolean;
transferEnabled: boolean;
invoiceEnabled: boolean;
gpuEnabled: boolean;
wechatEnabled: boolean;
stripeEnabled: boolean;
currency: 'shellCoin' | 'cny' | 'usd';
stripePromise: ReturnType<typeof loadStripe>;
setRechargeEnabled: (enabled: boolean) => void;
setTransferEnabled: (enabled: boolean) => void;
setInvoiceEnabled: (enabled: boolean) => void;
setGpuEnabled: (enabled: boolean) => void;
setStripe: (pub: string) => void;
setEnv: <T extends Exclude<keyof EnvState, 'setEnv' | 'setStripe' | 'stripePromise'>>(
key: T,
value: EnvState[T]
) => void;
};
const useEnvStore = create<EnvState>((set, get) => ({
rechargeEnabled: false,
transferEnabled: false,
invoiceEnabled: false,
wechatEnabled: false,
stripeEnabled: false,
gpuEnabled: false,
currency: 'shellCoin',
stripePromise: Promise.resolve(null),
setRechargeEnabled: (enabled: boolean) => set({ rechargeEnabled: enabled }),
setTransferEnabled: (enabled: boolean) => set({ transferEnabled: enabled }),
setInvoiceEnabled: (enabled: boolean) => set({ invoiceEnabled: enabled }),
setGpuEnabled(enabled) {
set({ gpuEnabled: enabled });
},
setStripe: (pub: string) => set({ stripePromise: loadStripe(pub) })
setStripe: (pub: string) => set({ stripePromise: loadStripe(pub) }),
setEnv: (k, v) => set({ [k]: v })
}));
export default useEnvStore;
@@ -1,6 +1,9 @@
export type EnvData = {
rechargeEnabled: boolean;
transferEnabled: boolean;
stripeEnabled: boolean;
wechatEnabled: boolean;
currency: 'shellCoin' | 'cny' | 'usd';
invoiceEnabled: boolean;
gpuEnabled: boolean;
stripePub: string;
@@ -7,6 +7,6 @@ module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh', 'zh-Hans'],
localeDetection: false,
},
}
localeDetection: false
}
};
+9 -9
View File
@@ -1,8 +1,8 @@
/** @type {import('next').NextConfig} */
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const { i18n } = require('./next-i18next.config')
const analyzer = process.env === 'production' ? [new BundleAnalyzerPlugin()] : []
const path = require('path')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const { i18n } = require('./next-i18next.config');
const analyzer = process.env === 'production' ? [new BundleAnalyzerPlugin()] : [];
const path = require('path');
const nextConfig = {
i18n,
output: 'standalone',
@@ -15,14 +15,14 @@ const nextConfig = {
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack']
}
])
config.plugins = [...config.plugins, ...analyzer]
return config
]);
config.plugins = [...config.plugins, ...analyzer];
return config;
},
experimental: {
// this includes files from the monorepo base two directories up
outputFileTracingRoot: path.join(__dirname, '../../')
}
}
};
module.exports = nextConfig
module.exports = nextConfig;
@@ -9,7 +9,8 @@ const langIcon = (
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true">
aria-hidden="true"
>
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z "
@@ -46,7 +47,8 @@ const LangSelect: React.FC<MenuButtonProps> = (props) => {
onClick={() => {
setLangStore(key);
i18n.changeLanguage(key);
}}>
}}
>
<Text>{lang.label}</Text>
</MenuItem>
))}
@@ -62,7 +62,8 @@ const Form = ({
w="200px"
className="template-dynamic-label"
color={'#333'}
userSelect={'none'}>
userSelect={'none'}
>
{item?.label}
{item?.required && (
<Text ml="2px" color={'#E53E3E'}>
@@ -92,14 +93,16 @@ const Form = ({
alignItems="center"
h={'160px'}
w={'100%'}
flexDirection="column">
flexDirection="column"
>
<Flex
border={'1px dashed #9CA2A8'}
borderRadius="50%"
w={'48px'}
h={'48px'}
justifyContent="center"
alignItems={'center'}>
alignItems={'center'}
>
<MyIcon color={'#7B838B'} name="empty"></MyIcon>
</Flex>
<Text mt={'12px'} fontSize={14} color={'#5A646E'}>
@@ -50,7 +50,8 @@ const Header = ({
h={'80px'}
mt={'32px'}
alignItems={'center'}
backgroundColor={'rgba(255, 255, 255, 0.90)'}>
backgroundColor={'rgba(255, 255, 255, 0.90)'}
>
<Flex
flexShrink={0}
alignItems={'center'}
@@ -59,7 +60,8 @@ const Header = ({
h={'80px'}
borderRadius={'8px'}
backgroundColor={'#fff'}
border={' 1px solid #DEE0E2'}>
border={' 1px solid #DEE0E2'}
>
<Image src={templateDetail?.spec?.icon} alt="" width={'60px'} height={'60px'} />
</Flex>
<Flex ml={'24px'} w="520px" flexDirection={'column'}>
@@ -81,7 +83,8 @@ const Header = ({
mt={'8px'}
fontSize={'12px'}
color={'5A646E'}
fontWeight={400}>
fontWeight={400}
>
{templateDetail?.spec?.description}
</Text>
</Flex>
@@ -95,7 +98,8 @@ const Header = ({
bg={'myWhite.600'}
borderColor={'myGray.200'}
variant={'base'}
onClick={handleExportYaml}>
onClick={handleExportYaml}
>
{t('Export')} Yaml
</Button>
<Button px={4} minW={'140px'} h={'40px'} variant={'primary'} onClick={applyCb}>
@@ -61,7 +61,8 @@ const ReadMe = ({ templateDetail }: { templateDetail: TemplateType }) => {
borderBottom={'1px solid #DEE0E2'}
color={'#24282C'}
fontSize={'18px'}
fontWeight={500}>
fontWeight={500}
>
<MyIcon name={'markdown'} mr={5} w={'24px'} h={'24px'} ml={'42px'} color={'myGray.500'} />
README.md
</Box>
@@ -69,7 +70,8 @@ const ReadMe = ({ templateDetail }: { templateDetail: TemplateType }) => {
<ReactMarkdown
linkTarget={'_blank'}
rehypePlugins={[rehypeRaw, [rehypeRewrite, { rewrite: myRewrite }]]}
remarkPlugins={[remarkGfm, remarkUnwrapImages]}>
remarkPlugins={[remarkGfm, remarkUnwrapImages]}
>
{templateReadMe}
</ReactMarkdown>
</Box>
@@ -25,7 +25,8 @@ const Yaml = ({ yamlList = [], pxVal }: { yamlList: YamlItemType[]; pxVal: numbe
overflow={'hidden'}
border={theme.borders.base}
borderRadius={'md'}
position={'relative'}>
position={'relative'}
>
<Flex px={8} py={4} bg={'myWhite.400'}>
<Box flex={1} fontSize={'xl'} color={'myGray.900'} fontWeight={'bold'}>
{yamlList[selectedIndex].filename}
@@ -34,7 +35,8 @@ const Yaml = ({ yamlList = [], pxVal }: { yamlList: YamlItemType[]; pxVal: numbe
cursor={'pointer'}
color={'myGray.600'}
_hover={{ color: '#219BF4' }}
onClick={() => copyData(yamlList[selectedIndex].value)}>
onClick={() => copyData(yamlList[selectedIndex].value)}
>
<MyIcon name="copy" w={'16px'} />
</Box>
</Flex>
@@ -250,7 +250,8 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
justifyContent={'start'}
alignItems={'center'}
backgroundColor={'rgba(255, 255, 255)'}
backdropBlur={'100px'}>
backdropBlur={'100px'}
>
<Box cursor={'pointer'} onClick={() => router.push('/')}>
<MyIcon ml={'46px'} name="arrowLeft" color={'#24282C'} w={'16px'} h={'16px'}></MyIcon>
</Box>
@@ -259,7 +260,8 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
fontWeight={500}
fontSize={16}
textDecoration={'none'}
color={'#7B838B'}>
color={'#7B838B'}
>
<BreadcrumbItem textDecoration={'none'}>
<BreadcrumbLink _hover={{ color: '#219BF4', textDecoration: 'none' }} href="/">
{t('Template List')}
@@ -278,7 +280,8 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
flexDirection={'column'}
width={'100%'}
flexGrow={1}
backgroundColor={'rgba(255, 255, 255, 0.90)'}>
backgroundColor={'rgba(255, 255, 255, 0.90)'}
>
<Header
templateDetail={templateDetail}
appName={''}
@@ -23,7 +23,8 @@ const BreadCrumbHeader = () => {
justifyContent={'start'}
alignItems={'center'}
backgroundColor={'rgba(255, 255, 255)'}
backdropBlur={'100px'}>
backdropBlur={'100px'}
>
<Box cursor={'pointer'} onClick={() => router.push('/')}>
<MyIcon name="arrowLeft" color={'#24282C'} w={'16px'} h={'16px'}></MyIcon>
</Box>
@@ -32,7 +33,8 @@ const BreadCrumbHeader = () => {
fontWeight={500}
fontSize={16}
textDecoration={'none'}
color={'#7B838B'}>
color={'#7B838B'}
>
<BreadcrumbItem textDecoration={'none'}>
<BreadcrumbLink _hover={{ color: '#219BF4', textDecoration: 'none' }} href="/">
{t('Template List')}
@@ -37,7 +37,8 @@ const Form = ({
w="200px"
className="template-dynamic-label"
color={'#333'}
userSelect={'none'}>
userSelect={'none'}
>
{item?.label}
{item?.required && (
<Text ml="2px" color={'#E53E3E'}>
@@ -67,14 +68,16 @@ const Form = ({
alignItems="center"
h={'100%'}
w={'100%'}
flexDirection="column">
flexDirection="column"
>
<Flex
border={'1px dashed #9CA2A8'}
borderRadius="50%"
w={'48px'}
h={'48px'}
justifyContent="center"
alignItems={'center'}>
alignItems={'center'}
>
<MyIcon color={'#7B838B'} name="empty"></MyIcon>
</Flex>
<Text mt={'12px'} fontSize={14} color={'#5A646E'}>
@@ -26,7 +26,8 @@ const Header = () => {
minW={'140px'}
bg={'myWhite.600'}
borderColor={'myGray.200'}
variant={'base'}>
variant={'base'}
>
{t('Export')} Yaml
</Button>
<Button px={4} minW={'140px'} h={'40px'} variant={'primary'}>
@@ -21,7 +21,8 @@ const YamlList = ({ yamlList = [] }: { yamlList: YamlItemType[] }) => {
alignItems={'start'}
zIndex={1}
position={'relative'}
overflow={'hidden'}>
overflow={'hidden'}
>
<Box flexShrink={0} mt={3} borderRadius={'sm'} overflow={'hidden'} bg={'white'}>
{yamlList.map((file, index) => (
<Box
@@ -46,7 +47,8 @@ const YamlList = ({ yamlList = [] }: { yamlList: YamlItemType[] }) => {
borderColor: 'myGray.200',
backgroundColor: 'transparent'
})}
onClick={() => setSelectedIndex(index)}>
onClick={() => setSelectedIndex(index)}
>
{file.filename}
</Box>
))}
@@ -57,7 +59,8 @@ const YamlList = ({ yamlList = [] }: { yamlList: YamlItemType[] }) => {
h="100%"
className={styles.codeBox}
flexDirection={'column'}
position={'relative'}>
position={'relative'}
>
{/* <Flex px={8} py={4} bg={'myWhite.400'}>
<Box flex={1} fontSize={'xl'} color={'myGray.900'} fontWeight={'bold'}>
{yamlList[selectedIndex].filename}
@@ -124,7 +124,8 @@ const Develop = () => {
borderRadius={'8px'}
overflowY={'hidden'}
overflowX={'scroll'}
flex={1}>
flex={1}
>
{/* left */}
<Flex flexDirection={'column'} flex={'0 1 500px'} borderRight={'1px solid #EFF0F1'}>
<Flex
@@ -134,7 +135,8 @@ const Develop = () => {
alignItems={'center'}
backgroundColor={'#F8FAFB'}
px="36px"
borderRadius={'8px 8px 0px 0px '}>
borderRadius={'8px 8px 0px 0px '}
>
<MyIcon name="dev" color={'#24282C'} w={'24px'} h={'24px'}></MyIcon>
<Text fontWeight={'500'} fontSize={'16px'} color={'#24282C'} ml="8px">
{t('develop.Development')}
@@ -162,7 +164,8 @@ const Develop = () => {
alignItems={'center'}
backgroundColor={'#F8FAFB'}
pl="42px"
borderRadius={'8px 8px 0px 0px '}>
borderRadius={'8px 8px 0px 0px '}
>
<MyIcon name="eyeShow" color={'#24282C'} w={'24px'} h={'24px'}></MyIcon>
<Text fontWeight={'500'} fontSize={'16px'} color={'#24282C'} ml="8px">
{t('develop.Preview')}
@@ -89,7 +89,8 @@ function Index() {
height={'100%'}
overflow={'auto'}
backgroundColor={'#edeff0'}
position={'relative'}>
position={'relative'}
>
<Flex justifyContent={'center'} flexDirection={'column'} alignItems={'center'} pt={'24px'}>
<Text color={'24282C'} fontSize={'48px'} fontWeight={500}>
{t('Templates')}
@@ -107,7 +108,8 @@ function Index() {
top={'26px'}
backgroundColor={'#FFF'}
borderRadius={'40px'}
onClick={() => router.push('/develop')}>
onClick={() => router.push('/develop')}
>
<MyIcon name="tool" fill={'transparent'} />
<Text ml="8px" color={'#485058'} fontWeight={500} cursor={'pointer'} fontSize={'12px'}>
{t('develop.YAML Detection Tool')}
@@ -122,7 +124,8 @@ function Index() {
width="24"
height="24"
viewBox="0 0 24 24"
fill="none">
fill="none"
>
<path
d="M21.71 20.29L18 16.61C19.4401 14.8144 20.1375 12.5353 19.9488 10.2413C19.7601 7.9473 18.6997 5.81278 16.9855 4.27664C15.2714 2.7405 13.0338 1.91951 10.7329 1.98247C8.43204 2.04543 6.24273 2.98756 4.61515 4.61514C2.98757 6.24272 2.04543 8.43203 1.98247 10.7329C1.91951 13.0338 2.74051 15.2714 4.27665 16.9855C5.81279 18.6997 7.94731 19.7601 10.2413 19.9488C12.5353 20.1375 14.8144 19.4401 16.61 18L20.29 21.68C20.383 21.7737 20.4936 21.8481 20.6154 21.8989C20.7373 21.9497 20.868 21.9758 21 21.9758C21.132 21.9758 21.2627 21.9497 21.3846 21.8989C21.5064 21.8481 21.617 21.7737 21.71 21.68C21.8902 21.4935 21.991 21.2443 21.991 20.985C21.991 20.7257 21.8902 20.4765 21.71 20.29ZM11 18C9.61553 18 8.26216 17.5895 7.11101 16.8203C5.95987 16.0511 5.06266 14.9579 4.53285 13.6788C4.00303 12.3997 3.86441 10.9922 4.13451 9.63436C4.4046 8.2765 5.07129 7.02922 6.05026 6.05025C7.02922 5.07128 8.2765 4.4046 9.63437 4.1345C10.9922 3.8644 12.3997 4.00303 13.6788 4.53284C14.9579 5.06265 16.0511 5.95986 16.8203 7.111C17.5895 8.26215 18 9.61553 18 11C18 12.8565 17.2625 14.637 15.9498 15.9497C14.637 17.2625 12.8565 18 11 18Z"
fill="#24282C"
@@ -144,7 +147,8 @@ function Index() {
justifyContent={'center'}
w={'100%'}
gridTemplateColumns="repeat(auto-fit, minmax(328px, 328px))"
gridGap={'24px'}>
gridGap={'24px'}
>
{filterData &&
filterData?.map((item: TemplateType) => {
return (
@@ -164,14 +168,16 @@ function Index() {
p={'24px'}
borderRadius={'6px'}
backgroundColor={'#fff'}
border={'1px solid #DEE0E2'}>
border={'1px solid #DEE0E2'}
>
<Box
p={'6px'}
w={'48px'}
h={'48px'}
borderRadius={'4px'}
backgroundColor={'#fff'}
border={' 1px solid #DEE0E2'}>
border={' 1px solid #DEE0E2'}
>
<Image src={item?.spec?.icon} alt="" width={'36px'} height={'36px'} />
</Box>
<Flex mt={'12px'} alignItems={'center'} justifyContent="space-between">
@@ -187,7 +193,8 @@ function Index() {
h={'28px'}
borderRadius={'4px'}
border={'1px solid #DEE0E2'}
backgroundColor={'#F4F6F8'}>
backgroundColor={'#F4F6F8'}
>
Deploy
</Flex>
</Flex>
@@ -203,7 +210,8 @@ function Index() {
mt={'8px'}
fontSize={'12px'}
color={'5A646E'}
fontWeight={400}>
fontWeight={400}
>
{item?.spec?.description}
</Text>
<Flex mt={'auto'} justifyContent={'space-between'} alignItems={'center'}>
+4 -14
View File
@@ -1,21 +1,11 @@
{
"compilerOptions": {
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"src/**/*.ts",
"src/**/*.tsx"
],
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx"],
"extends": "../../tsconfig.web.json"
}
}