diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 6e48aa7038f..7293969493d 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1475,6 +1475,9 @@ importers: '@prisma/client': specifier: ^5.10.2 version: 5.10.2(prisma@5.10.2) + '@sealos/driver': + specifier: workspace:^ + version: link:../../packages/driver '@sealos/ui': specifier: workspace:^ version: link:../../packages/ui @@ -14299,7 +14302,7 @@ packages: debug: 3.2.7 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.13.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.13.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.0) transitivePeerDependencies: - supports-color dev: true diff --git a/frontend/providers/devbox/.eslintrc.json b/frontend/providers/devbox/.eslintrc.json index bffb357a712..cce8c3061f3 100644 --- a/frontend/providers/devbox/.eslintrc.json +++ b/frontend/providers/devbox/.eslintrc.json @@ -1,3 +1,23 @@ { - "extends": "next/core-web-vitals" -} + "extends": "next/core-web-vitals", + "rules": { + // Consistently import navigation APIs from `@/i18n` + "no-restricted-imports": [ + "error", + { + "name": "next/link", + "message": "Please import from `@/i18n` instead." + }, + { + "name": "next/navigation", + "importNames": [ + "redirect", + "permanentRedirect", + "useRouter", + "usePathname" + ], + "message": "Please import from `@/i18n` instead." + } + ] + } +} \ No newline at end of file diff --git a/frontend/providers/devbox/api/platform.ts b/frontend/providers/devbox/api/platform.ts index 684763129e7..57f020171e3 100644 --- a/frontend/providers/devbox/api/platform.ts +++ b/frontend/providers/devbox/api/platform.ts @@ -1,6 +1,7 @@ import { GET, POST } from '@/services/request' -import type { UserQuotaItemType } from '@/types/user' +import type { UserQuotaItemType, UserTask } from '@/types/user' import type { Env } from '@/types/static' +import { getDesktopSessionFromSessionStorage, getSessionFromSessionStorage } from '@/utils/user' export const getAppEnv = () => GET('/api/getEnv') export const getUserQuota = () => @@ -14,3 +15,13 @@ export const getResourcePrice = () => GET('/api/platform/resourcePrice') export const postAuthCname = (data: { publicDomain: string; customDomain: string }) => POST('/api/platform/authCname', data) + +export const getUserTasks = () => + POST<{ needGuide: boolean; task: UserTask }>('/api/guide/getTasks', { + desktopToAppToken: getDesktopSessionFromSessionStorage()?.token + }) + +export const checkUserTask = () => + POST('/api/guide/checkTask', { + desktopToAppToken: getDesktopSessionFromSessionStorage()?.token + }) diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx index 2a588dd5358..0264056cc81 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxList.tsx @@ -25,7 +25,6 @@ const DevboxList = ({ devboxList: DevboxListItemTypeV2[] refetchDevboxList: () => void }) => { - const router = useRouter() const t = useTranslations() const { message: toast } = useMessage() @@ -133,153 +132,153 @@ const DevboxList = ({ key: string render?: (item: DevboxListItemTypeV2) => JSX.Element }[] = [ - { - title: t('name'), - key: 'name', - render: (item) => { - return ( - - {item.id} - - {item.name} - - - ) - } - }, - { - title: t('status'), - key: 'status', - render: (item) => - }, - { - title: t('create_time'), - dataIndex: 'createTime', - key: 'createTime', - render: (item) => { - return {item.createTime} - } - }, - { - title: t('cpu'), - key: 'cpu', - render: (item) => ( - - - - - {item?.usedCpu?.yData[item?.usedCpu?.yData?.length - 1]}% - + { + title: t('name'), + key: 'name', + render: (item) => { + return ( + + {item.id} + + {item.name} - + ) - }, - { - title: t('memory'), - key: 'storage', - render: (item) => ( - - - - - {item?.usedMemory?.yData[item?.usedMemory?.yData?.length - 1]}% - - + } + }, + { + title: t('status'), + key: 'status', + render: (item) => + }, + { + title: t('create_time'), + dataIndex: 'createTime', + key: 'createTime', + render: (item) => { + return {item.createTime} + } + }, + { + title: t('cpu'), + key: 'cpu', + render: (item) => ( + + + + + {item?.usedCpu?.yData[item?.usedCpu?.yData?.length - 1]}% + - ) - }, - { - title: t('control'), - key: 'control', - render: (item) => ( - - - - - - - } - menuList={[ - { - child: ( - <> - - {t('publish')} - - ), - onClick: () => handleOpenRelease(item) - }, - { - child: ( - <> - - {t('terminal')} - - ), - onClick: () => handleGoToTerminal(item), - menuItemStyle: { - borderBottomLeftRadius: '0px', - borderBottomRightRadius: '0px', - borderBottom: '1px solid #F0F1F6' - } - }, - { - child: ( - <> - - {t('update')} - - ), - onClick: () => router.push(`/devbox/create?name=${item.name}`) - }, - ...(item.status.value === 'Stopped' - ? [ + + ) + }, + { + title: t('memory'), + key: 'storage', + render: (item) => ( + + + + + {item?.usedMemory?.yData[item?.usedMemory?.yData?.length - 1]}% + + + + ) + }, + { + title: t('control'), + key: 'control', + render: (item) => ( + + + + + + + } + menuList={[ + { + child: ( + <> + + {t('publish')} + + ), + onClick: () => handleOpenRelease(item) + }, + { + child: ( + <> + + {t('terminal')} + + ), + onClick: () => handleGoToTerminal(item), + menuItemStyle: { + borderBottomLeftRadius: '0px', + borderBottomRightRadius: '0px', + borderBottom: '1px solid #F0F1F6' + } + }, + { + child: ( + <> + + {t('update')} + + ), + onClick: () => router.push(`/devbox/create?name=${item.name}`) + }, + ...(item.status.value === 'Stopped' + ? [ { child: ( <> @@ -290,10 +289,10 @@ const DevboxList = ({ onClick: () => handleStartDevbox(item) } ] - : []), - // maybe Error or other status,all can restart - ...(item.status.value !== 'Stopped' - ? [ + : []), + // maybe Error or other status,all can restart + ...(item.status.value !== 'Stopped' + ? [ { child: ( <> @@ -304,9 +303,9 @@ const DevboxList = ({ onClick: () => handleRestartDevbox(item) } ] - : []), - ...(item.status.value === 'Running' - ? [ + : []), + ...(item.status.value === 'Running' + ? [ { child: ( <> @@ -317,28 +316,28 @@ const DevboxList = ({ onClick: () => handlePauseDevbox(item) } ] - : []), - { - child: ( - <> - - {t('delete')} - - ), - menuItemStyle: { - _hover: { - color: 'red.600', - bg: 'rgba(17, 24, 36, 0.05)' - } - }, - onClick: () => setDelDevbox(item) - } - ]} - /> - - ) - } - ] + : []), + { + child: ( + <> + + {t('delete')} + + ), + menuItemStyle: { + _hover: { + color: 'red.600', + bg: 'rgba(17, 24, 36, 0.05)' + } + }, + onClick: () => setDelDevbox(item) + } + ]} + /> + + ) + } + ] return ( <> diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx index de65b83c3a5..3f0f3d90937 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/DevboxListContainer.tsx @@ -6,11 +6,12 @@ import { DevboxListItemTypeV2 } from '@/types/devbox' import { isElementInViewport } from '@/utils/tools' import { Flex, FlexProps } from '@chakra-ui/react' import { useQuery, useQueryClient } from '@tanstack/react-query' -import { useRouter } from 'next/navigation' +import { useRouter } from '@/i18n' import { useCallback, useEffect, useRef } from 'react' import DevboxHeader from './DevboxHeader' import DevboxList from './DevboxList' import Empty from './Empty' +import useListDriver from '@/hooks/useListDriver' function useDevboxList() { const queryClient = useQueryClient() @@ -19,25 +20,21 @@ function useDevboxList() { const { isOpen: templateIsOpen } = useTemplateStore() const list = useRef(devboxList) - const { isLoading, refetch: refetchDevboxList } = useQuery( - ['devboxListQuery'], - setDevboxList, - { - onSettled(res) { - if (!res) return - // refreshList(res) - list.current = res - }, - refetchInterval: !templateIsOpen ? 3000 : false, - staleTime: 3000, - enabled:!templateIsOpen, - } - ) + const { isLoading, refetch: refetchDevboxList } = useQuery(['devboxListQuery'], setDevboxList, { + onSettled(res) { + if (!res) return + // refreshList(res) + list.current = res + }, + refetchInterval: !templateIsOpen ? 3000 : false, + staleTime: 3000, + enabled: !templateIsOpen + }) const getViewportDevboxes = (minCount = 3) => { const doms = document.querySelectorAll('.devboxListItem') const viewportDomIds = Array.from(doms) .filter(isElementInViewport) - .map(item => item.getAttribute('data-id')) + .map((item) => item.getAttribute('data-id')) return viewportDomIds.length < minCount ? devboxList @@ -46,7 +43,6 @@ function useDevboxList() { useQuery( ['intervalLoadPods', devboxList.length], () => { - const viewportDevboxList = getViewportDevboxes() return viewportDevboxList .filter((devbox) => devbox.status.value !== 'Stopped') @@ -56,7 +52,7 @@ function useDevboxList() { refetchOnMount: true, refetchInterval: !templateIsOpen ? 3000 : false, staleTime: 3000, - enabled: !isLoading &&!templateIsOpen, + enabled: !isLoading && !templateIsOpen } ) @@ -71,7 +67,7 @@ function useDevboxList() { { refetchInterval: !templateIsOpen ? 2 * 60 * 1000 : false, staleTime: 2 * 60 * 1000, - enabled:!isLoading &&!templateIsOpen, + enabled: !isLoading && !templateIsOpen } ) // 路由预加载 @@ -91,23 +87,23 @@ function useDevboxList() { export default function DevboxListContainer({ ...props }: FlexProps) { const { list, isLoading, refetchList } = useDevboxList() + const { handleUserGuide } = useListDriver() + + useEffect(() => { + if (list.length > 0) { + handleUserGuide() + } + }, [list.length]) + return ( - + {list.length === 0 && !isLoading ? ( ) : ( - + )} {/* */} ) -} \ No newline at end of file +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx index b322cf15d3b..512eaadd1d2 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/components/Empty.tsx @@ -4,7 +4,6 @@ import { useTranslations } from 'next-intl' import MyIcon from '@/components/Icon' import { useRouter } from '@/i18n' - const Empty = () => { const router = useRouter() const t = useTranslations() diff --git a/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx b/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx index 12608f29ce2..0e7c9b21cf5 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/(home)/page.tsx @@ -1,5 +1,5 @@ -import DevboxListContainer from "./components/DevboxListContainer" +import DevboxListContainer from './components/DevboxListContainer' -export default function EmptyPage() { +export default async function EmptyPage() { return } diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx index b301d0bdb72..18a9a22629d 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositoryListNav.tsx @@ -1,9 +1,9 @@ -import MyIcon from "@/components/Icon"; -import { TemplateState } from "@/constants/template"; -import { useTemplateStore } from "@/stores/template"; -import { Box, Flex, Text } from "@chakra-ui/react"; -import { useTranslations } from "next-intl"; -import { usePathname } from "next/navigation"; +import MyIcon from '@/components/Icon' +import { TemplateState } from '@/constants/template' +import { useTemplateStore } from '@/stores/template' +import { Box, Flex, Text } from '@chakra-ui/react' +import { useTranslations } from 'next-intl' +import { usePathname } from '@/i18n' const TemplateRepositoryListNav = () => { const t = useTranslations() @@ -15,8 +15,7 @@ const TemplateRepositoryListNav = () => { // width="168px" height="18px" gap="12px" - alignItems="center" - > + alignItems="center"> {/* All Templates Tab */} { cursor="pointer" onClick={() => { openTemplateModal({ - 'templateState': TemplateState.publicTemplate, + templateState: TemplateState.publicTemplate, lastRoute }) - }} - > + }}> { fontWeight="500" lineHeight="16px" letterSpacing="0.5px" - color="#485264" - > - {t("all_templates")} + color="#485264"> + {t('all_templates')} {/* Divider */} - + {/* My Templates Tab */} { cursor="pointer" onClick={() => { openTemplateModal({ - 'templateState': TemplateState.privateTemplate, + templateState: TemplateState.privateTemplate, lastRoute }) - }} - > - + }}> + - {t("my_templates")} + color="#485264"> + {t('my_templates')} - ); -}; + ) +} -export default TemplateRepositoryListNav; \ No newline at end of file +export default TemplateRepositoryListNav diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx index b0e7ebaa212..77f7078b2a8 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx @@ -11,49 +11,70 @@ import { useFormContext } from 'react-hook-form' import Label from '../../Label' import TemplateRepositoryListNav from '../TemplateRepositoryListNav' import TemplateRepositoryItem from './TemplateReposistoryItem' +import useDriver from '@/hooks/useDriver' interface TemplateRepositorySelectorProps { isEdit: boolean } -export default function TemplateRepositorySelector({ - isEdit, -}: TemplateRepositorySelectorProps) { +export default function TemplateRepositorySelector({ isEdit }: TemplateRepositorySelectorProps) { const { startedTemplate, setStartedTemplate } = useDevboxStore() const { setValue, getValues, watch } = useFormContext() const t = useTranslations() - const templateRepositoryQuery = useQuery(['list-official-template-repository'], listOfficialTemplateRepository, { - staleTime: Infinity, - cacheTime: 1000 * 60 * 30 - }) + const { handleUserGuide } = useDriver() + + const templateRepositoryQuery = useQuery( + ['list-official-template-repository'], + listOfficialTemplateRepository, + { + onSuccess(res) { + console.log('res', res) + handleUserGuide() + }, + staleTime: Infinity, + cacheTime: 1000 * 60 * 30 + } + ) const curTemplateRepositoryUid = watch('templateRepositoryUid') - const curTemplateRepositoryDetail = useQuery(['get-template-repository', curTemplateRepositoryUid], () => { - return getTemplateRepository(curTemplateRepositoryUid) - }, { - enabled: !!isEdit && !!curTemplateRepositoryUid, - }) + const curTemplateRepositoryDetail = useQuery( + ['get-template-repository', curTemplateRepositoryUid], + () => { + return getTemplateRepository(curTemplateRepositoryUid) + }, + { + enabled: !!isEdit && !!curTemplateRepositoryUid + } + ) - const templateData = useMemo(() => templateRepositoryQuery.data?.templateRepositoryList || [], [templateRepositoryQuery.data]) + const templateData = useMemo( + () => templateRepositoryQuery.data?.templateRepositoryList || [], + [templateRepositoryQuery.data] + ) const categorizedData = useMemo(() => { - return templateData.reduce((acc, item) => { - acc[item.kind] = [...(acc[item.kind] || []), item] - return acc - }, { - 'LANGUAGE': [], - 'FRAMEWORK': [], - 'OS': [], - 'CUSTOM': [] - } as Record) + return templateData.reduce( + (acc, item) => { + acc[item.kind] = [...(acc[item.kind] || []), item] + return acc + }, + { + LANGUAGE: [], + FRAMEWORK: [], + OS: [], + CUSTOM: [] + } as Record + ) }, [templateData]) useEffect(() => { if (!startedTemplate || isEdit) { return } const templateUid = startedTemplate.uid - if (templateData.findIndex((item) => { - return item.uid === templateUid - }) > -1) { + if ( + templateData.findIndex((item) => { + return item.uid === templateUid + }) > -1 + ) { setStartedTemplate(undefined) } setValue('templateRepositoryUid', templateUid) @@ -63,9 +84,14 @@ export default function TemplateRepositorySelector({ if (startedTemplate || isEdit) { return } - if (!(templateRepositoryQuery.isSuccess - && templateData.length > 0 - && templateRepositoryQuery.isFetched)) return + if ( + !( + templateRepositoryQuery.isSuccess && + templateData.length > 0 && + templateRepositoryQuery.isFetched + ) + ) + return const curTemplateRepositoryUid = getValues('templateRepositoryUid') const curTemplateRepository = templateData.find((item) => { return item.uid === curTemplateRepositoryUid @@ -74,49 +100,78 @@ export default function TemplateRepositorySelector({ const defaultTemplateRepositoryUid = templateData[0].uid setValue('templateRepositoryUid', defaultTemplateRepositoryUid) } - }, [templateRepositoryQuery.isSuccess, startedTemplate, templateData, templateRepositoryQuery.isFetched, isEdit]) + }, [ + templateRepositoryQuery.isSuccess, + startedTemplate, + templateData, + templateRepositoryQuery.isFetched, + isEdit + ]) useEffect(() => { - if (!isEdit || !templateRepositoryQuery.isSuccess || !templateData || !curTemplateRepositoryDetail.isSuccess || !curTemplateRepositoryDetail.data) { + if ( + !isEdit || + !templateRepositoryQuery.isSuccess || + !templateData || + !curTemplateRepositoryDetail.isSuccess || + !curTemplateRepositoryDetail.data + ) { return } const templateRepository = curTemplateRepositoryDetail.data.templateRepository // setStartedTemplate(templateRepository) setValue('templateRepositoryUid', templateRepository.uid) - if (templateData.findIndex((item) => { - return item.uid === templateRepository.uid - }) === -1) { + if ( + templateData.findIndex((item) => { + return item.uid === templateRepository.uid + }) === -1 + ) { setStartedTemplate(templateRepository) } - }, [curTemplateRepositoryDetail.isSuccess, curTemplateRepositoryDetail.data, curTemplateRepositoryDetail.isFetched, isEdit, templateData, templateRepositoryQuery.isSuccess]) + }, [ + curTemplateRepositoryDetail.isSuccess, + curTemplateRepositoryDetail.data, + curTemplateRepositoryDetail.isFetched, + isEdit, + templateData, + templateRepositoryQuery.isSuccess + ]) return ( - - - - - {!!startedTemplate && - {t('current')} - - + + + + - } - - {/* Language */} - {categorizedData['LANGUAGE'].length !== 0 && {t('language')}} - - {categorizedData['LANGUAGE']?.map((item) => ( - - ))} + + {!!startedTemplate && ( + + {t('current')} + + + + + )} + + {/* Language */} + {categorizedData['LANGUAGE'].length !== 0 && {t('language')}} + + {categorizedData['LANGUAGE']?.map((item) => ( + + ))} + - + {/* Framework */} {categorizedData['FRAMEWORK'].length !== 0 && {t('framework')}} @@ -138,4 +193,3 @@ export default function TemplateRepositorySelector({ ) } - diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx index 6c5c712c3bb..68e0a7ee4e3 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/index.tsx @@ -1,31 +1,36 @@ -import MyIcon from "@/components/Icon" -import { Box, BoxProps } from "@chakra-ui/react" -import { useTranslations } from "next-intl" -import ConfigurationHeader from "../ConfigurationHeader" -import CpuSelector from "./CpuSelector" -import DevboxNameInput from "./DevboxNameInput" -import MemorySelector from "./MemorySelector" -import TemplateRepositorySelector from "./TemplateRepositorySelector" -import TemplateSelector from "./TemplateSelector" +import MyIcon from '@/components/Icon' +import { Box, BoxProps } from '@chakra-ui/react' +import { useTranslations } from 'next-intl' +import ConfigurationHeader from '../ConfigurationHeader' +import CpuSelector from './CpuSelector' +import DevboxNameInput from './DevboxNameInput' +import MemorySelector from './MemorySelector' +import TemplateRepositorySelector from './TemplateRepositorySelector' +import TemplateSelector from './TemplateSelector' export default function BasicConfiguration({ isEdit, ...props }: BoxProps & { isEdit: boolean }) { const t = useTranslations() - return - - - {t('basic_configuration')} - - - {/* Devbox Name */} - - {/* Template Repository */} - - {/* Runtime Version */} - - {/* CPU */} - - {/* Memory */} - + return ( + + + + {t('basic_configuration')} + + + {/* Devbox Name */} + + {/* Template Repository */} + + {/* Runtime Version */} + + + + {/* CPU */} + + {/* Memory */} + + + - -} \ No newline at end of file + ) +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx index 72e699922eb..b4bbcfbe21b 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/NetworkConfiguration/index.tsx @@ -1,15 +1,25 @@ -import MyIcon from "@/components/Icon" -import { ProtocolList } from "@/constants/devbox" -import { useEnvStore } from "@/stores/env" -import { DevboxEditTypeV2 } from "@/types/devbox" -import { nanoid } from "@/utils/tools" -import { Box, BoxProps, Button, ButtonProps, Flex, IconButton, Input, Switch, useTheme } from "@chakra-ui/react" -import { MySelect, useMessage } from "@sealos/ui" -import { useTranslations } from "next-intl" -import dynamic from "next/dynamic" -import { useState } from "react" -import { useFieldArray, useFormContext } from "react-hook-form" -import ConfigurationHeader from "../ConfigurationHeader" +import MyIcon from '@/components/Icon' +import { ProtocolList } from '@/constants/devbox' +import { useEnvStore } from '@/stores/env' +import { DevboxEditTypeV2 } from '@/types/devbox' +import { nanoid } from '@/utils/tools' +import { + Box, + BoxProps, + Button, + ButtonProps, + Flex, + IconButton, + Input, + Switch, + useTheme +} from '@chakra-ui/react' +import { MySelect, useMessage } from '@sealos/ui' +import { useTranslations } from 'next-intl' +import dynamic from 'next/dynamic' +import { useState } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import ConfigurationHeader from '../ConfigurationHeader' // const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 12) export type CustomAccessModalParams = { @@ -20,21 +30,18 @@ export type CustomAccessModalParams = { const CustomAccessModal = dynamic(() => import('@/components/modals/CustomAccessModal')) const AppendNetworksButton = (props: ButtonProps) => { const t = useTranslations() - return () + return ( + + ) } export default function NetworkConfiguration({ isEdit, ...props }: BoxProps & { isEdit: boolean }) { - const { - register, - getValues, - control - } = useFormContext() + const { register, getValues, control } = useFormContext() const theme = useTheme() const [customAccessModalData, setCustomAccessModalData] = useState() const { env } = useEnvStore() @@ -61,181 +68,183 @@ export default function NetworkConfiguration({ isEdit, ...props }: BoxProps & { }) } // const networks = watch('networks') - return <> - - - {t('Network Configuration')} - - - {networks.length === 0 && ( - appendNetworks()} /> - )} - {networks.map((network, i) => ( - - - - {t('Container Port')} - - { - const ports = getValues('networks').map((network, index) => ({ - port: network.port, - index - })); - // 排除当前正在编辑的端口 - const isDuplicate = ports.some( - (item) => item.port === value && item.index !== i - ); - return !isDuplicate || t('The port number cannot be repeated'); - } - } - })} - /> - {i === networks.length - 1 && networks.length < 5 && ( - - appendNetworks()} /> + return ( + <> + + + + {t('Network Configuration')} + + + {networks.length === 0 && appendNetworks()} />} + {networks.map((network, i) => ( + + + + {t('Container Port')} + + { + const ports = getValues('networks').map((network, index) => ({ + port: network.port, + index + })) + // 排除当前正在编辑的端口 + const isDuplicate = ports.some( + (item) => item.port === value && item.index !== i + ) + return !isDuplicate || t('The port number cannot be repeated') + } + } + })} + /> + {i === networks.length - 1 && networks.length < 5 && ( + + appendNetworks()} /> + + )} - )} - - - - {t('Open Public Access')} - - - { - const devboxName = getValues('name') - if (!devboxName) { - toast({ - title: t('Please enter the devbox name first'), - status: 'warning' - }) - return - } - updateNetworks(i, { - ...getValues('networks')[i], - networkName: network.networkName || `${devboxName}-${nanoid()}`, - protocol: network.protocol || 'HTTP', - openPublicDomain: e.target.checked, - publicDomain: network.publicDomain || `${nanoid()}.${env.ingressDomain}` - }) - }} - /> - - - {network.openPublicDomain && ( - <> - - + + + {t('Open Public Access')} + - { + { + const devboxName = getValues('name') + if (!devboxName) { + toast({ + title: t('Please enter the devbox name first'), + status: 'warning' + }) + return + } updateNetworks(i, { ...getValues('networks')[i], - protocol: val + networkName: network.networkName || `${devboxName}-${nanoid()}`, + protocol: network.protocol || 'HTTP', + openPublicDomain: e.target.checked, + publicDomain: network.publicDomain || `${nanoid()}.${env.ingressDomain}` }) }} /> - - - {network.customDomain ? network.customDomain : network.publicDomain!} - - - setCustomAccessModalData({ - publicDomain: network.publicDomain!, - customDomain: network.customDomain! - }) - }> - {t('Custom Domain')} - - - - )} - {networks.length >= 1 && ( - - - } - onClick={() => removeNetworks(i)} - /> - - )} - - ))} - - - {!!customAccessModalData && ( - setCustomAccessModalData(undefined)} - onSuccess={(e) => { - const i = networks.findIndex( - (item) => item.publicDomain === customAccessModalData.publicDomain - ) - if (i === -1) return - updateNetworks(i, { - ...networks[i], - customDomain: e - }) - setCustomAccessModalData(undefined) - }} - /> - )} -} \ No newline at end of file + {network.openPublicDomain && ( + <> + + + + { + updateNetworks(i, { + ...getValues('networks')[i], + protocol: val + }) + }} + /> + + + {network.customDomain ? network.customDomain : network.publicDomain!} + + + setCustomAccessModalData({ + publicDomain: network.publicDomain!, + customDomain: network.customDomain! + }) + }> + {t('Custom Domain')} + + + + + + )} + {networks.length >= 1 && ( + + + } + onClick={() => removeNetworks(i)} + /> + + )} + + ))} + + + {!!customAccessModalData && ( + setCustomAccessModalData(undefined)} + onSuccess={(e) => { + const i = networks.findIndex( + (item) => item.publicDomain === customAccessModalData.publicDomain + ) + if (i === -1) return + updateNetworks(i, { + ...networks[i], + customDomain: e + }) + setCustomAccessModalData(undefined) + }} + /> + )} + + ) +} diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx index edfcd924487..4743877d35e 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/index.tsx @@ -1,11 +1,6 @@ 'use client' -import { - Box, - Flex, - Grid, - useTheme -} from '@chakra-ui/react' +import { Box, Flex, Grid, useTheme } from '@chakra-ui/react' import { Tabs } from '@sealos/ui' import { throttle } from 'lodash' import { useTranslations } from 'next-intl' @@ -24,19 +19,11 @@ import { obj2Query } from '@/utils/tools' import BasicConfiguration from './BasicConfiguration' import NetworkConfiguration from './NetworkConfiguration' -const Form = ({ - pxVal, - isEdit -}: { - pxVal: number - isEdit: boolean -}) => { +const Form = ({ pxVal, isEdit }: { pxVal: number; isEdit: boolean }) => { const theme = useTheme() const router = useRouter() const t = useTranslations() - const { - watch - } = useFormContext() + const { watch } = useFormContext() const navList: { id: string; label: string; icon: string }[] = [ { id: 'baseInfo', @@ -79,7 +66,6 @@ const Form = ({ // eslint-disable-next-line }, []) - const boxStyles = { border: theme.borders.base, borderRadius: 'lg', @@ -88,100 +74,100 @@ const Form = ({ } return ( - - {/* left sidebar */} - - + {/* left sidebar */} + + + router.replace( + `/devbox/create?${obj2Query({ + type: 'yaml' + })}` + ) + } + /> + + {navList.map((item) => ( + { + setActiveNav(item.id) + window.location.hash = item.id + }}> + + + + {item.label} + + + ))} + + + + + + - router.replace( - `/devbox/create?${obj2Query({ - type: 'yaml' - })}` - ) - } /> - - {navList.map((item) => ( - { - setActiveNav(item.id) - window.location.hash = item.id - }}> - - - - {item.label} - - - ))} - - - - - - - - - {/* right content */} - - {/* base info */} - - {/* network */} - - + + {/* right content */} + + {/* base info */} + + {/* network */} + + + ) } diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx index e0c6940625b..3aac4e86e37 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/page.tsx @@ -35,7 +35,7 @@ import { debounce } from 'lodash' const ErrorModal = dynamic(() => import('@/components/modals/ErrorModal')) const DevboxCreatePage = () => { - const {env } = useEnvStore() + const { env } = useEnvStore() const generateDefaultYamlList = () => generateYamlList(defaultDevboxEditValueV2, env) const router = useRouter() const t = useTranslations() @@ -94,7 +94,7 @@ const DevboxCreatePage = () => { // updateyamlList every time yamlList change const debouncedUpdateYaml = useMemo( - () => + () => debounce((data: DevboxEditTypeV2, env) => { try { const newYamlList = generateYamlList(data, env) @@ -119,7 +119,6 @@ const DevboxCreatePage = () => { } }, [formHook, debouncedUpdateYaml, env]) - useQuery( ['initDevboxCreateData'], () => { @@ -193,8 +192,8 @@ const DevboxCreatePage = () => { isClosable: true }) } - if(!parsedNewYamlList) { - // prevent empty yamlList + if (!parsedNewYamlList) { + // prevent empty yamlList return setErrorMessage(t('submit_form_error')) } const patch = patchYamlList({ @@ -223,9 +222,7 @@ const DevboxCreatePage = () => { console.log('error', error) if (error instanceof String && error.includes('402')) { setErrorMessage(t('outstanding_tips')) - } - else - setErrorMessage(JSON.stringify(error)) + } else setErrorMessage(JSON.stringify(error)) } setIsLoading(false) } @@ -250,38 +247,42 @@ const DevboxCreatePage = () => { }) }, [formHook.formState.errors, toast, t]) - return (<> - - -
- formHook.handleSubmit((data) => openConfirm(() => submitSuccess(data))(), submitError)() - } - /> - - {tabType === 'form' ? ( -
- ) : ( - - )} - - - - - + return ( + <> + + +
+ formHook.handleSubmit( + (data) => openConfirm(() => submitSuccess(data))(), + submitError + )() + } + /> + + {tabType === 'form' ? ( + + ) : ( + + )} + + + + + - {!!errorMessage && ( - setErrorMessage('')} /> - )} - + {!!errorMessage && ( + setErrorMessage('')} /> + )} + ) } diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx index ec53313842d..8e920f49e10 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Header.tsx @@ -144,7 +144,7 @@ const Header = ({ {/* detail button */} - + {!isLargeScreen && ( - {t('memory')}{' '} + {t('memory')} {devboxDetail?.usedMemory?.yData[devboxDetail?.usedMemory?.yData?.length - 1]}% diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Version.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Version.tsx index 1695f78ba02..2950d9c3b7c 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Version.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/detail/[name]/components/Version.tsx @@ -1,9 +1,11 @@ +'use client' + import { Box, Button, Flex, MenuButton, Text, useDisclosure } from '@chakra-ui/react' import { SealosMenu, useMessage } from '@sealos/ui' import { useQuery } from '@tanstack/react-query' import { customAlphabet } from 'nanoid' import { useTranslations } from 'next-intl' -import { useCallback, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { sealosApp } from 'sealos-desktop-sdk/app' import { delDevboxVersionByName, getAppsByDevboxId } from '@/api/devbox' @@ -27,10 +29,12 @@ import { useDevboxStore } from '@/stores/devbox' import { useEnvStore } from '@/stores/env' import { AppListItemType } from '@/types/app' import { parseTemplateConfig } from '@/utils/tools' +import useReleaseDriver from '@/hooks/useReleaseDriver' const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 6) const Version = () => { + const { startReleaseGuide } = useReleaseDriver() const t = useTranslations() const { message: toast } = useMessage() const { Loading, setIsLoading } = useLoading() @@ -73,6 +77,13 @@ const Version = () => { enabled: !!devbox } ) + + useEffect(() => { + if (devboxVersionList?.length && devboxVersionList.length > 0) { + startReleaseGuide() + } + }, [devboxVersionList.length]) + const listPrivateTemplateRepositoryQuery = useQuery( ['template-repository-list', 'template-repository-private'], () => { @@ -230,6 +241,7 @@ const Version = () => { render: (item: DevboxVersionListItemType) => (