diff --git a/frontend/providers/applaunchpad/src/utils/adapt.ts b/frontend/providers/applaunchpad/src/utils/adapt.ts index ac55ebbea..5c5021829 100644 --- a/frontend/providers/applaunchpad/src/utils/adapt.ts +++ b/frontend/providers/applaunchpad/src/utils/adapt.ts @@ -271,9 +271,9 @@ export const adaptAppDetail = (configs: DeployKindsType[]): AppDetailType => { protocol: (ingress?.metadata?.annotations?.[ 'nginx.ingress.kubernetes.io/backend-protocol' - ] as AppEditType['networks'][0]['protocol']) || - item?.protocol || - 'HTTP', + ] as AppEditType['networks'][0]['protocol']) || item.protocol === 'TCP' + ? 'HTTP' + : (item.protocol as AppEditType['networks'][number]['protocol']), openPublicDomain: !!ingress, ...(domain.endsWith(SEALOS_DOMAIN) ? { diff --git a/frontend/providers/cloudserver/i18nrc.json b/frontend/providers/cloudserver/i18nrc.json index 470912080..c39846f7f 100644 --- a/frontend/providers/cloudserver/i18nrc.json +++ b/frontend/providers/cloudserver/i18nrc.json @@ -3,7 +3,6 @@ "entryLocale": "zh", "output": "./public/locales", "outputLocales": [ - "en", - "jp" + "en" ] } \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/locales/en/common.json b/frontend/providers/cloudserver/public/locales/en/common.json index 8d885a008..ab9ab10db 100644 --- a/frontend/providers/cloudserver/public/locales/en/common.json +++ b/frontend/providers/cloudserver/public/locales/en/common.json @@ -49,7 +49,7 @@ "Prompt": "Prompt", "Public IP": "Public IP", "Public network bandwidth": "Public network bandwidth", - "Quantity is 1 to 100": "Quantity is 1 to 100", + "Quantity tips": "Quantity is {{amount1}} to {{amount2}}", "ReStart": "Restart", "Reference fee": "Reference fee", "Reference fee disk tips": "{{price}}/GiB/hour", @@ -70,6 +70,7 @@ "Supports changing the operating system": "Note: After purchase, you can change the operating system according to your business needs", "SystemDisk": "System Disk", "The maximum number of instances is 100": "Maximum number of instances is 100", + "The maximum number of instances is 20": "The maximum number is 20", "The maximum number of instances is 500": "Maximum number of instances is 500", "The minimum number of instances is 1": "Minimum number of instances is 1", "The minimum number of instances is 20": "Minimum number of instances is 20", @@ -87,5 +88,23 @@ "sshPassword": "SSH Password", "storage fees": "Storage fees", "success": "Success", - "terminal": "Terminal" + "terminal": "Terminal", + "Billing model": "Billing model", + "Availability Zone": "Availability Zone", + "Architecture": "Architecture", + "Instance Family": "Instance Family", + "postPaidByHour": "Pay as you go", + "heterogeneous": "Heterogeneous", + "prePaid": "Yearly and monthly subscription", + "x86_64": "x86_64", + "costEffective": "Cost Effective", + "gpu": "GPU", + "Password does not meet requirements": "Password does not meet requirements", + "Lowercase letters az": "Lowercase letters a ~ z", + "Capital letters az": "Capital letters A ~ Z", + "Numbers 09": "Numbers 0 ~ 9", + "Contains at least three of these": "Contains at least three of these", + "Cannot start with": "Cannot start with \"/\"", + "cannot contain spaces": "cannot contain spaces", + "Within 8 to 30 characters": "Within 8 to 30 characters (12 or more characters recommended)" } \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/locales/jp/common.json b/frontend/providers/cloudserver/public/locales/jp/common.json deleted file mode 100644 index fa19905ac..000000000 --- a/frontend/providers/cloudserver/public/locales/jp/common.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "Add Data Disk": "データディスクを追加する", - "Amount": "数量", - "Are you sure to create a cloud host?": "クラウドホストを作成してもよろしいですか", - "Assign Public IP": "パブリックIPを割り当てる", - "Auto Generate Password": "パスワードを自動生成する", - "Automatic Generated": "自動生成", - "BandWidth": "帯域幅", - "Bandwidth by hour": "(時間ごとの帯域幅)", - "Basic Config": "基本設定", - "Billing items": "請求項目", - "Billing rules": "請求規則", - "CPU": "CPU", - "Cancel": "キャンセル", - "Changing": "変更中", - "Cloud host list": "クラウドホストリスト", - "Configuration fee details": "設定料金の詳細", - "Confirm": "確認", - "Copy Failed": "コピーに失敗しました", - "Create Time": "作成時間", - "Created": "作成済み", - "Creating": "作成中", - "Creation Time": "作成時間", - "DataDisk": "データディスク", - "Delete": "削除", - "Deleted": "削除済み", - "Deleting": "削除中", - "Deletion warning": "削除警告", - "Describe": "説明", - "Details": "詳細", - "Disk Size": "ディスクサイズ", - "Empty List": "空のリスト", - "Fee inquiry in progress": "料金照会中...", - "GPU": "GPU", - "IP Address": "IPアドレス", - "Image": "イメージ", - "Instance": "インスタンス", - "Login Method": "ログイン方法", - "Memory": "メモリ", - "Name": "名前", - "New Server": "新しいサーバー", - "Operate": "操作", - "Operating System": "オペレーティングシステム", - "Operation": "操作", - "Password copied to clipboard": "パスワードがクリップボードにコピーされました", - "Please enter": "入力してください", - "Please enter your password": "パスワードを入力してください", - "Please select a mirror": "ミラーを選択してください", - "Prompt": "プロンプト", - "Public IP": "パブリックIP", - "Public network bandwidth": "パブリックネットワーク帯域幅", - "Quantity is 1 to 100": "数量は1から100までです", - "ReStart": "再起動", - "Reference fee": "参考料金", - "Reference fee disk tips": "{{price}}/GiB/時間あたり", - "Reference fee network tips": "パブリックネットワークの帯域幅が5 Mbps未満の場合、価格は{{price1}}/Mbps/時間です。帯域幅が5 Mbps以上の場合、価格は{{price2}}/Mbps/時間です。", - "Reference fee tip": "{{price}}/時間", - "Restarting": "再起動中", - "Set Password": "パスワードを設定する", - "Start": "開始", - "Started": "実行中", - "Starting": "起動中", - "Status": "ステータス", - "Stop": "停止", - "Stopped": "停止済み", - "Stopping": "停止中", - "Storage": "ストレージ", - "Submit": "提出する", - "Submit Error": "エラーを送信する", - "Supports changing the operating system": "注意:購入後、オペレーティングシステムの変更がサポートされます。ビジネスのニーズに応じてシステムを変更できます", - "SystemDisk": "システムディスク", - "The maximum number of instances is 100": "最大値は100です", - "The maximum number of instances is 500": "最大値は500です", - "The minimum number of instances is 1": "最小値は1です", - "The minimum number of instances is 20": "最小値は20です", - "The number of instances cannot be empty": "インスタンスの数は空にできません", - "The purpose of the disk": "ディスクの目的", - "Type": "タイプ", - "capacity": "容量", - "close cloud server tip": "クラウドサーバーをシャットダウンしても、ストレージの料金は発生しますので、ご確認ください!", - "delete cloud server tip": "このクラウドサーバーを削除してもよろしいですか", - "hour": "時間", - "price": "価格", - "publicIpAssigned tips": "注意:独立したパブリックIPが割り当てられていないため、外部との通信にはインターネットIPを使用することができません。サーバーは登録に使用できませんので、慎重に選択してください。", - "ssh port": "SSHポート", - "sshName": "SSHユーザー名", - "sshPassword": "SSHパスワード", - "storage fees": "ストレージ料金", - "success": "成功", - "terminal": "ターミナル" -} \ No newline at end of file diff --git a/frontend/providers/cloudserver/public/locales/zh/common.json b/frontend/providers/cloudserver/public/locales/zh/common.json index 62cef639e..e26146080 100644 --- a/frontend/providers/cloudserver/public/locales/zh/common.json +++ b/frontend/providers/cloudserver/public/locales/zh/common.json @@ -69,7 +69,7 @@ "Are you sure to create a cloud host?": "确认创建一台云主机吗", "Confirm": "确认", "close cloud server tip": "请注意,关机状态下存储仍会收费,请确认!", - "Quantity is 1 to 100": "数量是1到100", + "Quantity tips": "数量是 {{amount1}} 到 {{amount2}}", "Password copied to clipboard": "密码已复制到剪切板", "Copy Failed": "复制失败", "Changing": "变更中", @@ -87,5 +87,24 @@ "hour": "小时", "Reference fee disk tips": "{{price}}/GiB/小时", "Reference fee network tips": "当公网带宽小于5 Mbps时,价格为{{price1}}/Mbps/小时;当公网带宽大于等于5 Mbps时,价格为{{price2}}/Mbps/小时。", - "Billing rules": "计费规则" -} + "Billing rules": "计费规则", + "The maximum number of instances is 20": "最大数量是20", + "Billing model": "计费模式", + "Availability Zone": "可用区", + "Architecture": "架构", + "Instance Family": "实例族", + "postPaidByHour": "按量计费", + "heterogeneous": "异构计算", + "prePaid": "包年包月", + "x86_64": "x86计算", + "costEffective": "实惠型", + "gpu": "GPU", + "Password does not meet requirements": "密码不符合要求", + "Lowercase letters az": "小写字母 a ~ z", + "Capital letters az": "大写字母 A ~ Z", + "Numbers 09": "数字 0 ~ 9", + "Contains at least three of these": "至少包含其中三项", + "Cannot start with": "不能以\" / \"开头", + "cannot contain spaces": "不能包含空格", + "Within 8 to 30 characters": "在 8 ~ 30 位字符数以内(推荐12位以上)" +} \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/api/cloudserver.ts b/frontend/providers/cloudserver/src/api/cloudserver.ts index 822da7cf5..daecd3f4f 100644 --- a/frontend/providers/cloudserver/src/api/cloudserver.ts +++ b/frontend/providers/cloudserver/src/api/cloudserver.ts @@ -1,10 +1,15 @@ +import { ServerTypePayload } from '@/pages/api/cloudserver/listType'; import { UpdateStatusPayload } from '@/pages/api/cloudserver/updateStatus'; import { POST } from '@/services/request'; import adaptCloudServerListItem from '@/types/adapt'; import { CloudServerPrice, CloudServerType, EditForm, OperatingSystems } from '@/types/cloudserver'; import { CVMInstanceType } from '@/types/cloudserver'; +import { CVMRegionType } from '@/types/region'; -export const getCloudServerType = () => POST('/api/cloudserver/listType'); +export const getCloudServerRegion = () => POST('/api/cloudserver/getRegion'); + +export const getCloudServerType = (payload: ServerTypePayload) => + POST('/api/cloudserver/listType', payload); export const getCloudServerImage = () => POST('/api/cloudserver/listImage'); diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/check.svg b/frontend/providers/cloudserver/src/components/Icon/icons/check.svg new file mode 100644 index 000000000..a644c51af --- /dev/null +++ b/frontend/providers/cloudserver/src/components/Icon/icons/check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg b/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg index 828a964b1..9b1a705b4 100644 --- a/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg +++ b/frontend/providers/cloudserver/src/components/Icon/icons/plus.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/frontend/providers/cloudserver/src/components/Icon/index.tsx b/frontend/providers/cloudserver/src/components/Icon/index.tsx index ad6b3ca1f..100923795 100644 --- a/frontend/providers/cloudserver/src/components/Icon/index.tsx +++ b/frontend/providers/cloudserver/src/components/Icon/index.tsx @@ -30,6 +30,7 @@ const map = { info: require('./icons/info.svg').default, restore: require('./icons/restore.svg').default, download: require('./icons/download.svg').default, + check: require('./icons/check.svg').default, close: require('./icons/close.svg').default, help: require('./icons/help.svg').default, link: require('./icons/link.svg').default diff --git a/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx b/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx index d51261c56..e691d7938 100644 --- a/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx +++ b/frontend/providers/cloudserver/src/components/MyTooltip/index.tsx @@ -5,7 +5,7 @@ const MyTooltip = ({ children, ...props }: TooltipProps) => { return ( { py={2} borderRadius={'8px'} whiteSpace={'pre-wrap'} + boxShadow={'1px 1px 10px rgba(0,0,0,0.2)'} {...props} > {children} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts index c8bbe0b8a..1900c084e 100644 --- a/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/create.ts @@ -12,13 +12,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const dataDisks = form.storages .filter(({ use }) => use === 'DataDisk') .flatMap(({ size, amount }) => Array(amount).fill(size)); - console.log(dataDisks); + const systemDiskSize = form.storages.find(({ use }) => use === 'SystemDisk')?.size || 20; const payload: CreateCloudServerPayload = { virtualMachinePackageFamily: form.virtualMachinePackageFamily, virtualMachinePackageName: form.virtualMachinePackageName, imageId: form.systemImageId, - systemDisk: form.systemDiskSize, + systemDisk: systemDiskSize, dataDisks: dataDisks, ...(form.publicIpAssigned ? { @@ -27,7 +27,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) : { internetMaxBandwidthOut: 0 }), loginPassword: form.password, loginName: '', - metaData: {} + metaData: {}, + zone: form.zone, + virtualMachineType: form.virtualMachineType, + virtualMachineArch: form.virtualMachineArch, + chareType: form.chargeType }; const { data } = await POST('/action/create', payload, { diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/getRegion.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/getRegion.ts new file mode 100644 index 000000000..32f74855f --- /dev/null +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/getRegion.ts @@ -0,0 +1,30 @@ +import { verifyAccessToken } from '@/services/backend/auth'; +import { jsonRes } from '@/services/backend/response'; +import { POST } from '@/services/requestLaf'; +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await verifyAccessToken(req); + + const { data, error } = await POST( + '/action/get-region-details', + {}, + { + headers: { + Authorization: req.headers.authorization + } + } + ); + + if (error) { + return jsonRes(res, { code: 500, error: error }); + } + + jsonRes(res, { + data: data + }); + } catch (error) { + jsonRes(res, { code: 500, error: error }); + } +} diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts index 0f7b06d06..77b3bf71e 100644 --- a/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/listImage.ts @@ -17,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } ); - return jsonRes(res, { + jsonRes(res, { data: data }); } catch (error) { diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts index 683444458..1a2b39e32 100644 --- a/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/listType.ts @@ -1,24 +1,36 @@ import { verifyAccessToken } from '@/services/backend/auth'; import { jsonRes } from '@/services/backend/response'; import { POST } from '@/services/requestLaf'; +import { CVMChargeType } from '@/types/region'; import { NextApiRequest, NextApiResponse } from 'next'; +export type ServerTypePayload = { + zone: string; + virtualMachinePackageFamily: string; + chargeType: CVMChargeType; + virtualMachineType: string; + virtualMachineArch: string; +}; + export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await verifyAccessToken(req); - const { data, error } = await POST( - '/action/get-virtual-machine-package', - {}, - { - headers: { - Authorization: req.headers.authorization - } - } - ); + const payload = req.body as ServerTypePayload; + console.log(payload); - return jsonRes(res, { - data: data || error + const { data, error } = await POST('/action/get-virtual-machine-package', payload, { + headers: { + Authorization: req.headers.authorization + } + }); + + if (error) { + return jsonRes(res, { code: 500, error: error }); + } + + jsonRes(res, { + data: data }); } catch (error) { jsonRes(res, { code: 500, error: error }); diff --git a/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts b/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts index 02e595223..773bacd90 100644 --- a/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts +++ b/frontend/providers/cloudserver/src/pages/api/cloudserver/price.ts @@ -12,12 +12,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const dataDisks = form.storages .filter(({ use }) => use === 'DataDisk') .flatMap(({ size, amount }) => Array(amount).fill(size)); + const systemDiskSize = form.storages.find(({ use }) => use === 'SystemDisk')?.size || 20; const payload: CreateCloudServerPayload = { virtualMachinePackageFamily: form.virtualMachinePackageFamily, virtualMachinePackageName: form.virtualMachinePackageName, imageId: form.systemImageId, - systemDisk: form.systemDiskSize, + systemDisk: systemDiskSize, dataDisks: dataDisks, ...(form.publicIpAssigned ? { @@ -26,7 +27,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) : { internetMaxBandwidthOut: 0 }), loginPassword: form.password, loginName: '', - metaData: {} + metaData: {}, + zone: form.zone, + virtualMachineType: form.virtualMachineType, + virtualMachineArch: form.virtualMachineArch, + chareType: form.chargeType }; const result = await POST('/action/get-price', payload, { diff --git a/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx index 5d9c9066c..b2cbdb178 100644 --- a/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx +++ b/frontend/providers/cloudserver/src/pages/cloudserver/create/components/Form.tsx @@ -1,33 +1,81 @@ -import { getCloudServerImage, getCloudServerType } from '@/api/cloudserver'; +import { getCloudServerImage, getCloudServerRegion, getCloudServerType } from '@/api/cloudserver'; import MyIcon from '@/components/Icon'; import { MyTable, TableColumnsType } from '@/components/MyTable'; +import MyTooltip from '@/components/MyTooltip'; import { CloudServerType, EditForm, StorageType } from '@/types/cloudserver'; +import { CVMArchType, CVMRegionType, CVMZoneType, VirtualMachineType } from '@/types/region'; import { Box, Button, + Center, Divider, Flex, FormControl, IconButton, Image, Input, + ListItem, Radio, Skeleton, Stack, Switch, + Tab, + TabList, + TabPanel, + TabPanels, + TabPanelsProps, + TabProps, + Tabs, Text, + UnorderedList, + useDisclosure, useTheme } from '@chakra-ui/react'; -import { MySelect, RangeInput, Tabs } from '@sealos/ui'; +import { MySelect, RangeInput, Tabs as SealosTabs } from '@sealos/ui'; import { useQuery } from '@tanstack/react-query'; import { customAlphabet } from 'nanoid'; import { useTranslation } from 'next-i18next'; import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react'; -import { UseFormReturn, useFieldArray } from 'react-hook-form'; +import { useFieldArray, useFormContext } from 'react-hook-form'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 4); const nanoidUpper = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 4); const nanoidNumber = customAlphabet('0123456789', 4); +const tabPanelStyles: TabPanelsProps = { + padding: '0px', + my: '24px' +}; + +const tabStyles: TabProps = { + bg: 'grayModern.50', + _hover: { + opacity: 0.9, + color: 'brightBlue.700', + borderColor: '#85ccff' + }, + border: '1px solid', + borderRadius: '2px', + cursor: 'pointer', + fontWeight: '400', + color: 'grayModern.900', + borderColor: 'grayModern.200', + width: '160px', + css: { + svg: { + opacity: 0 + } + }, + _selected: { + bg: '#F9FDFE', + borderColor: 'brightBlue.400', + svg: { + opacity: 1, + color: 'brightBlue.600' + } + }, + mr: '8px' +}; + const Label = ({ children, w = 120, @@ -48,37 +96,253 @@ const Label = ({ ); -export default function Form({ - formHook, - refresh, +function VirtualMachinePackageTabs({ + virtualMachinePackageFamily +}: { + virtualMachinePackageFamily: string[]; +}) { + const formHook = useFormContext(); + const index = virtualMachinePackageFamily?.findIndex( + (item) => item === formHook?.getValues('virtualMachinePackageFamily') + ); + const { t } = useTranslation(); + return ( + { + const item = virtualMachinePackageFamily[e]; + formHook?.setValue('virtualMachinePackageFamily', item); + }} + > + + + {virtualMachinePackageFamily?.map((item) => ( + + {item} + + + ))} + + + ); +} + +function VirtualMachineTabs({ virtualMachine }: { virtualMachine: VirtualMachineType[] }) { + const formHook = useFormContext(); + const index = virtualMachine?.findIndex( + (item) => item.virtualMachineType === formHook?.getValues('virtualMachineType') + ); + const { t } = useTranslation(); + + return ( + { + const item = virtualMachine[e]; + formHook?.setValue('virtualMachineType', item.virtualMachineType); + formHook?.setValue('virtualMachinePackageFamily', item.virtualMachinePackageFamily[0]); + }} + > + + + {virtualMachine?.map((item) => ( + + {t(item.virtualMachineType)} + + + ))} + + + {virtualMachine?.map((item) => ( + + + + ))} + + + ); +} + +function ArchTabs({ arch }: { arch: CVMArchType[] }) { + const formHook = useFormContext(); + const index = arch?.findIndex((item) => item.arch === formHook?.getValues('virtualMachineArch')); + const { t } = useTranslation(); + return ( + { + const item = arch[e]; + formHook?.setValue('virtualMachineArch', item.arch); + formHook?.setValue('virtualMachineType', item.virtualMachineType[0].virtualMachineType); + formHook?.setValue( + 'virtualMachinePackageFamily', + item.virtualMachineType[0].virtualMachinePackageFamily[0] + ); + }} + > + + + {arch?.map((item) => ( + + {t(item.arch)} + + + ))} + + + {arch?.map((item) => ( + + + + ))} + + + ); +} + +function ZoneTabs({ zone }: { zone: CVMZoneType[] }) { + const formHook = useFormContext(); + const { t } = useTranslation(); + const index = zone?.findIndex((item) => item.zone === formHook?.getValues('zone')); + + return ( + + + + {zone?.map((item) => ( + { + formHook?.setValue('zone', item.zone); + }} + > + {item.zone} + + + ))} + + + {zone?.map((item) => ( + + + + ))} + + + ); +} + +function OuterTabs({ systemRegion }: { systemRegion: CVMRegionType[] }) { + const formHook = useFormContext(); + const { t } = useTranslation(); + const index = systemRegion?.findIndex( + (item) => item.chargeType === formHook?.getValues('chargeType') + ); + + return ( + { + const item = systemRegion[e]; + formHook?.setValue('chargeType', item.chargeType); + }} + > + + + {systemRegion?.map((item) => ( + + {t(item.chargeType)} + + + ))} + + + {systemRegion?.map((item) => ( + + + + ))} + + + ); +} + +export default function Form({ + refresh, setInstanceType }: { - formHook: UseFormReturn; refresh: boolean; - setInstanceType: Dispatch>; }) { + const formHook = useFormContext(); if (!formHook) return <>; const [clientRender, setClientRender] = useState(false); + const { isOpen, onOpen, onClose } = useDisclosure(); + + const specialTips = useMemo(() => { + return { + specialChars: "()`~!@#$%^&*-+=_|{}[]:;'<>,.?/", + regex: + /^(?!.*\s)(?!\/)(?=[^\/]{8,30}$)(?:(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])|(?=.*[a-z])(?=.*[A-Z])(?=.*[\(\)`~!@#$%^&*\-+=_|{}\[\]:;'<>,.?/])|(?=.*[a-z])(?=.*[0-9])(?=.*[\(\)`~!@#$%^&*\-+=_|{}\[\]:;'<>,.?/])|(?=.*[A-Z])(?=.*[0-9])(?=.*[\(\)`~!@#$%^&*\-+=_|{}\[\]:;'<>,.?/])).*$/ + }; + }, []); useEffect(() => { setClientRender(true); }, []); - const { data: SystemImage } = useQuery(['getCloudServerImage'], getCloudServerImage, { + const { data: systemRegion } = useQuery(['getCloudServerRegion'], getCloudServerRegion, { + staleTime: 5 * 60 * 1000 + }); + const { data: systemImage } = useQuery(['getCloudServerImage'], getCloudServerImage, { staleTime: 5 * 60 * 1000 }); - const { data: ServersData } = useQuery(['getCloudServerType'], getCloudServerType, { - staleTime: 5 * 60 * 1000, - onSuccess(data) { - if (data?.[0]) { - formHook.setValue('virtualMachinePackageName', data[0].virtualMachinePackageName); - setInstanceType(data?.[0]); + const { data: serverTypeData } = useQuery( + [ + 'getCloudServerType', + formHook.getValues('zone'), + formHook.getValues('virtualMachinePackageFamily'), + formHook.getValues('chargeType'), + formHook.getValues('virtualMachineType'), + formHook.getValues('virtualMachineArch') + ], + () => + getCloudServerType({ + zone: formHook.getValues('zone'), + virtualMachinePackageFamily: formHook.getValues('virtualMachinePackageFamily'), + chargeType: formHook.getValues('chargeType'), + virtualMachineType: formHook.getValues('virtualMachineType'), + virtualMachineArch: formHook.getValues('virtualMachineArch') + }), + { + staleTime: 5 * 60 * 1000, + onSuccess(data) { + if (data?.[0]) { + formHook.setValue('virtualMachinePackageName', data[0].virtualMachinePackageName); + setInstanceType(data?.[0]); + } } } - }); + ); const theme = useTheme(); const { t } = useTranslation(); @@ -170,7 +434,7 @@ export default function Form({ value={getValues(`storages.${rowNumber}.size`)} min={20} max={500} - // hoverText={t('Number of instances: 1 to 20') || 'Number of instances: 1 to 20'} + hoverText={t('Quantity tips', { amount1: 20, amount2: 500 }) || ''} setVal={(val) => { register(`storages.${rowNumber}.size`, { required: @@ -216,7 +480,7 @@ export default function Form({ value={getValues(`storages.${rowNumber}.amount`)} min={1} max={20} - // hoverText={t('Number of instances: 1 to 20') || 'Number of instances: 1 to 20'} + hoverText={t('Quantity tips', { amount1: 1, amount2: 20 }) || ''} setVal={(val) => { register(`storages.${rowNumber}.amount`, { required: @@ -281,16 +545,17 @@ export default function Form({ + - {ServersData ? ( + {serverTypeData ? ( { setValue('virtualMachinePackageName', item.virtualMachinePackageName); @@ -305,38 +570,42 @@ export default function Form({ )} + {/* systemImage */} - {SystemImage && - Object.keys(SystemImage)?.map((key) => { + {systemImage && + Object.keys(systemImage)?.map((key) => { return ( - + ); })} @@ -362,8 +631,8 @@ export default function Form({ width={'400px'} value={getValues('systemImageId')} list={ - SystemImage && getValues('system') - ? SystemImage[getValues('system')].images.map((item) => { + systemImage && getValues('system') + ? systemImage[getValues('system')].images.map((item) => { return { value: item.id, label: `${item.os} | ${item.version} | ${item.architect}` @@ -385,6 +654,7 @@ export default function Form({ + {/* systemStorage */} - {MockInstance && MockInstance?.length > 0 ? ( + {data?.list && data?.list?.length > 0 ? ( ) : ( - {t('Empty List')} + {t('Empty List')} + )} diff --git a/frontend/providers/cloudserver/src/types/cloudserver.ts b/frontend/providers/cloudserver/src/types/cloudserver.ts index 596abb6ed..f2465c42f 100644 --- a/frontend/providers/cloudserver/src/types/cloudserver.ts +++ b/frontend/providers/cloudserver/src/types/cloudserver.ts @@ -1,7 +1,8 @@ +import { CVMChargeType } from './region'; + export type EditForm = { system: string; systemImageId: string; - systemDiskSize: number; publicIpAssigned: boolean; internetMaxBandWidthOut: number; password: string; @@ -9,13 +10,17 @@ export type EditForm = { storages: StorageType[]; virtualMachinePackageFamily: string; virtualMachinePackageName: string; + virtualMachineType: string; + virtualMachineArch: string; + chargeType: CVMChargeType; + zone: string; }; export type StorageType = { type?: string; size: number; amount: number; - use: string; + use: 'SystemDisk' | 'DataDisk'; }; export type CloudServerType = { @@ -137,4 +142,8 @@ export type CreateCloudServerPayload = { metaData: { [key: string]: any; }; + zone: string; + virtualMachineType: string; + virtualMachineArch: string; + chareType: CVMChargeType; }; diff --git a/frontend/providers/cloudserver/src/types/region.ts b/frontend/providers/cloudserver/src/types/region.ts new file mode 100644 index 000000000..a7923e65b --- /dev/null +++ b/frontend/providers/cloudserver/src/types/region.ts @@ -0,0 +1,26 @@ +export type VirtualMachineType = { + virtualMachineType: string; + virtualMachinePackageFamily: string[]; +}; + +export type CVMArchType = { + arch: string; + virtualMachineType: VirtualMachineType[]; +}; + +export type CVMZoneType = { + zone: string; + arch: CVMArchType[]; +}; + +export enum CVMChargeType { + postPaidByHour = 'postPaidByHour', + prePaid = 'prePaid' +} + +export type CVMRegionType = { + regionId: string; + chargeType: CVMChargeType; + regionName: string; + zone: CVMZoneType[]; +}; diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx index 7a8ed56ac..470c760d9 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx @@ -90,7 +90,7 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { }@${host}:${port}`; if (db?.dbType === 'mongodb' || db?.dbType === 'postgresql') { - connection += '?directConnection=true'; + connection += '/?directConnection=true'; } return {