diff --git a/react/src/App.tsx b/react/src/App.tsx index 3e2da8daf0..6817c474ce 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -28,7 +28,7 @@ const ServingPage = React.lazy(() => import('./pages/ServingPage')); const EndpointDetailPage = React.lazy( () => import('./pages/EndpointDetailPage'), ); -// const SummaryPage = React.lazy(() => import('./pages/SummaryPage')); +const StartPage = React.lazy(() => import('./pages/StartPage')); const EnvironmentPage = React.lazy(() => import('./pages/EnvironmentPage')); const MyEnvironmentPage = React.lazy(() => import('./pages/MyEnvironmentPage')); const StorageHostSettingPage = React.lazy( @@ -61,9 +61,9 @@ const ComputeSessionList = React.lazy( () => import('./components/ComputeSessionList'), ); -const RedirectToSummary = () => { +const RedirectToStart = () => { useSuspendedBackendaiClient(); - const pathName = '/summary'; + const pathName = '/start'; document.dispatchEvent( new CustomEvent('move-to-from-react', { detail: { @@ -72,7 +72,7 @@ const RedirectToSummary = () => { }, }), ); - return ; + return ; }; const router = createBrowserRouter([ @@ -109,17 +109,26 @@ const router = createBrowserRouter([ children: [ { path: '/', - element: , + element: , }, { //for electron dev mode path: '/build/electron-app/app/index.html', - element: , + element: , }, { //for electron prod mode path: '/app/index.html', - element: , + element: , + }, + { + path: '/start', + element: ( + + + + ), + handle: { labelKey: 'webui.menu.Start' }, }, { path: '/summary', @@ -134,7 +143,6 @@ const router = createBrowserRouter([ style={{ marginBottom: token.paddingContentVerticalLG }} closable /> - {/* */} ); }, diff --git a/react/src/components/ActionItemContent.tsx b/react/src/components/ActionItemContent.tsx new file mode 100644 index 0000000000..5b1460f50b --- /dev/null +++ b/react/src/components/ActionItemContent.tsx @@ -0,0 +1,122 @@ +import Flex from './Flex'; +import { Button, Divider, Typography, theme } from 'antd'; +import { useEffect, useRef, useState } from 'react'; + +interface StartItemContentProps { + title: string; + description?: string; + icon?: React.ReactNode; + buttonText: string; + onClick?: () => void; + themeColor?: string; + iconBgColor?: string; +} + +const ActionItemContent: React.FC = ({ + title, + description, + icon, + buttonText, + onClick, + themeColor, + iconBgColor, +}) => { + const { token } = theme.useToken(); + const [needScroll, setNeedScroll] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + const handleResize = () => { + if (containerRef.current) { + const { clientWidth } = containerRef.current; + // Currently, the minimum width for non-scrollable content is 220px (Start an Interactive Session Card) + if (clientWidth <= 225) { + setNeedScroll(true); + } else { + setNeedScroll(false); + } + } + }; + handleResize(); + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + return ( + + + + {icon} + + + + {title} + + + + {!needScroll && description} + + + + {description && ( + + )} + + + + + + ); +}; + +export default ActionItemContent; diff --git a/react/src/components/BAIBoard.tsx b/react/src/components/BAIBoard.tsx index 4f75ed034d..e0ae218e44 100644 --- a/react/src/components/BAIBoard.tsx +++ b/react/src/components/BAIBoard.tsx @@ -1,7 +1,6 @@ -import Flex from './Flex'; import Board, { BoardProps } from '@cloudscape-design/board-components/board'; import BoardItem from '@cloudscape-design/board-components/board-item'; -import { Skeleton, Typography } from 'antd'; +import { Skeleton } from 'antd'; import { createStyles } from 'antd-style'; import { Suspense } from 'react'; @@ -23,29 +22,31 @@ const useStyles = createStyles(({ css }) => { board: css` ${defaultBoard} `, - disableCustomize: css` - ${defaultBoard} - .bai_board_handle { + disableResize: css` + .bai_board_resizer { display: none !important; } - .bai_board_resizer { + `, + disableMove: css` + .bai_board_handle { display: none !important; } .bai_board_header { - height: var(--token-boardHeaderHeight, 55px) !important; + display: none !important; } `, boardItems: css` & > div:first-child { - border: 1px solid var(--token-colorBorder) !important ; + border: none !important ; border-radius: var(--token-borderRadius) !important ; background-color: var(--token-colorBgContainer) !important ; } & > div:first-child > div:first-child > div:first-child { - border-bottom: 1px solid var(--token-colorBorder) !important; margin-bottom: var(--token-margin); background-color: var(--token-colorBgContainer) !important ; + position: absolute; + z-index: 1; } `, }; @@ -56,19 +57,27 @@ interface BAICustomizableGridProps { onItemsChange: ( event: CustomEvent>, ) => void; - customizable?: boolean; + resizable?: boolean; + movable?: boolean; } const BAIBoard: React.FC = ({ items: parsedItems, - customizable = false, + resizable = false, + movable = false, ...BoardProps }) => { const { styles } = useStyles(); + + const boardStyles = [ + styles.board, + !movable && styles.disableMove, + !resizable && styles.disableResize, + ].join(' '); + return ( { return ( @@ -82,11 +91,6 @@ const BAIBoard: React.FC = ({ resizeHandleAriaLabel: '', resizeHandleAriaDescription: '', }} - header={ - - {item.data.title} - - } > }> {item.data.content} diff --git a/react/src/components/MainLayout/WebUISider.tsx b/react/src/components/MainLayout/WebUISider.tsx index ae2351eeaf..cbcfd36876 100644 --- a/react/src/components/MainLayout/WebUISider.tsx +++ b/react/src/components/MainLayout/WebUISider.tsx @@ -19,6 +19,7 @@ import { FileDoneOutlined, HddOutlined, InfoCircleOutlined, + PlayCircleOutlined, RocketOutlined, SolutionOutlined, ToolOutlined, @@ -70,6 +71,11 @@ const WebUISider: React.FC = (props) => { const [isOpenSignoutModal, { toggle: toggleSignoutModal }] = useToggle(false); const generalMenu = filterEmptyItem([ + { + label: t('webui.menu.Start'), + icon: , + key: 'start', + }, { label: t('webui.menu.Summary'), icon: , diff --git a/react/src/components/SummaryPageItems/SummaryItemDownloadApp.tsx b/react/src/components/SummaryPageItems/SummaryItemDownloadApp.tsx deleted file mode 100644 index b2e487d2ce..0000000000 --- a/react/src/components/SummaryPageItems/SummaryItemDownloadApp.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useSuspendedBackendaiClient } from '../../hooks'; -import Flex from '../Flex'; -import { Button, Select, Typography, theme } from 'antd'; -import { useState } from 'react'; - -const detectDefaultOS = () => { - if (navigator.userAgent.indexOf('Mac') !== -1) return 'MacOS'; - if (navigator.userAgent.indexOf('Win') !== -1) return 'Windows'; - if (navigator.userAgent.indexOf('Linux') !== -1) return 'Linux'; - return 'MacOS'; -}; - -const SummaryItemDownloadApp: React.FC = () => { - const { token } = theme.useToken(); - const [OS, setOS] = useState(detectDefaultOS()); - - const baiClient = useSuspendedBackendaiClient(); - const url = baiClient._config.appDownloadUrl; - const windowOS = baiClient.supports('use-win-instead-of-win32') - ? 'win' - : 'win32'; - - const appDownloadMap: Record = { - Linux: { - os: 'linux', - architecture: ['arm64', 'x64'], - extension: 'zip', - }, - MacOS: { - os: 'macos', - architecture: ['arm64', 'x64'], - extension: 'dmg', - }, - Windows: { - os: windowOS, - architecture: ['arm64', 'x64'], - extension: 'zip', - }, - }; - const downloadApp = (architecture: string) => { - //@ts-ignore - const pkgVersion = globalThis.packageVersion; - const os = appDownloadMap[OS].os; - const extension = appDownloadMap[OS].extension; - const downloadLink = `${url}/v${pkgVersion}/backend.ai-desktop-${pkgVersion}-${os}-${architecture}.${extension}`; - window.open(downloadLink, '_blank'); - }; - - return ( - -