@@ -2,6 +2,8 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
22import { useTranslation } from 'react-i18next' ;
33import { useNavigate , useParams } from 'react-router-dom' ;
44import { debounce } from 'lodash' ;
5+ import { ExpandableSection , Tabs } from '@cloudscape-design/components' ;
6+ import Wizard from '@cloudscape-design/components/wizard' ;
57
68import {
79 Box ,
@@ -11,7 +13,6 @@ import {
1113 Container ,
1214 Header ,
1315 Hotspot ,
14- InfoLink ,
1516 Loader ,
1617 Popover ,
1718 SelectCSD ,
@@ -20,10 +21,12 @@ import {
2021} from 'components' ;
2122import { HotspotIds } from 'layouts/AppLayout/TutorialPanel/constants' ;
2223
23- import { useBreadcrumbs , useHelpPanel , useNotifications } from 'hooks' ;
24+ import { useBreadcrumbs , useNotifications } from 'hooks' ;
2425import { riseRouterException } from 'libs' ;
26+ import { copyToClipboard } from 'libs' ;
2527import { ROUTES } from 'routes' ;
2628import { useGetProjectQuery , useUpdateProjectMembersMutation , useUpdateProjectMutation } from 'services/project' ;
29+ import { useGetRunsQuery } from 'services/run' ;
2730import { useGetUserDataQuery } from 'services/user' ;
2831
2932import { useCheckAvailableProjectPermission } from 'pages/Project/hooks/useCheckAvailableProjectPermission' ;
@@ -37,7 +40,6 @@ import { BackendsTable } from '../../Backends/Table';
3740import { GatewaysTable } from '../../Gateways' ;
3841import { useGatewaysTable } from '../../Gateways/hooks' ;
3942import { ProjectSecrets } from '../../Secrets' ;
40- import { CLI_INFO } from './constants' ;
4143
4244import styles from './styles.module.scss' ;
4345
@@ -46,7 +48,7 @@ export const ProjectSettings: React.FC = () => {
4648 const params = useParams ( ) ;
4749 const navigate = useNavigate ( ) ;
4850 const paramProjectName = params . projectName ?? '' ;
49- const [ openHelpPanel ] = useHelpPanel ( ) ;
51+ const [ isExpandedCliSection , setIsExpandedCliSection ] = React . useState ( false ) ;
5052 const [ configCliCommand , copyCliCommand ] = useConfigProjectCliCommand ( { projectName : paramProjectName } ) ;
5153
5254 const { isAvailableDeletingPermission, isProjectManager, isProjectAdmin, isAvailableProjectManaging } =
@@ -60,6 +62,15 @@ export const ProjectSettings: React.FC = () => {
6062
6163 const { data, isLoading, error } = useGetProjectQuery ( { name : paramProjectName } ) ;
6264
65+ const { data : runsData } = useGetRunsQuery ( {
66+ project_name : paramProjectName ,
67+ limit : 1 ,
68+ } ) ;
69+
70+ useEffect ( ( ) => {
71+ setIsExpandedCliSection ( ! runsData || runsData . length === 0 ) ;
72+ } , [ runsData ] ) ;
73+
6374 useEffect ( ( ) => {
6475 if ( error && 'status' in error && error . status === 404 ) {
6576 riseRouterException ( ) ;
@@ -167,6 +178,8 @@ export const ProjectSettings: React.FC = () => {
167178 } ) ;
168179 } ;
169180
181+ const [ activeStepIndex , setActiveStepIndex ] = React . useState ( 0 ) ;
182+
170183 if ( isLoadingPage )
171184 return (
172185 < Container >
@@ -179,42 +192,163 @@ export const ProjectSettings: React.FC = () => {
179192 { data && backendsData && gatewaysData && (
180193 < SpaceBetween size = "l" >
181194 { isProjectMember && (
182- < Container
183- header = {
184- < Header variant = "h2" info = { < InfoLink onFollow = { ( ) => openHelpPanel ( CLI_INFO ) } /> } >
185- { t ( 'projects.edit.cli' ) }
186- </ Header >
195+ < ExpandableSection
196+ variant = "container"
197+ headerText = "CLI"
198+ expanded = { isExpandedCliSection }
199+ onChange = { ( { detail } ) => setIsExpandedCliSection ( detail . expanded ) }
200+ headerActions = {
201+ < Button
202+ iconName = "script"
203+ variant = { isExpandedCliSection ? 'normal' : 'primary' }
204+ onClick = { ( ) => setIsExpandedCliSection ( ( prev ) => ! prev ) }
205+ />
187206 }
207+ // headerInfo={<InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />}
188208 >
189- < SpaceBetween size = "s" >
190- < Box variant = "p" color = "text-body-secondary" >
191- Run the following commands to set up the CLI for this project
192- </ Box >
193-
194- < div className = { styles . codeWrapper } >
195- < Hotspot hotspotId = { HotspotIds . CONFIGURE_CLI_COMMAND } >
196- < Code className = { styles . code } > { configCliCommand } </ Code >
197-
198- < div className = { styles . copy } >
199- < Popover
200- dismissButton = { false }
201- position = "top"
202- size = "small"
203- triggerType = "custom"
204- content = { < StatusIndicator type = "success" > { t ( 'common.copied' ) } </ StatusIndicator > }
205- >
206- < Button
207- formAction = "none"
208- iconName = "copy"
209- variant = "normal"
210- onClick = { copyCliCommand }
209+ < Wizard
210+ i18nStrings = { {
211+ stepNumberLabel : ( stepNumber ) => `Step ${ stepNumber } ` ,
212+ collapsedStepsLabel : ( stepNumber , stepsCount ) => `Step ${ stepNumber } of ${ stepsCount } ` ,
213+ skipToButtonLabel : ( step ) => `Skip to ${ step . title } ` ,
214+ navigationAriaLabel : 'Steps' ,
215+ // cancelButton: "Cancel",
216+ previousButton : 'Previous' ,
217+ nextButton : 'Next' ,
218+ optional : 'required' ,
219+ } }
220+ onNavigate = { ( { detail } ) => setActiveStepIndex ( detail . requestedStepIndex ) }
221+ activeStepIndex = { activeStepIndex }
222+ onSubmit = { ( ) => setIsExpandedCliSection ( false ) }
223+ submitButtonText = "Dismiss"
224+ allowSkipTo = { true }
225+ steps = { [
226+ {
227+ title : 'Install CLI' ,
228+ // info: <InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />,
229+ description : 'To use dstack, install the CLI on your local machine.' ,
230+ content : (
231+ < Hotspot hotspotId = { HotspotIds . INSTALL_CLI_COMMAND } >
232+ < Tabs
233+ variant = "stacked"
234+ tabs = { [
235+ {
236+ label : 'uv' ,
237+ id : 'uv' ,
238+ content : (
239+ < >
240+ < div className = { styles . codeWrapper } >
241+ < Code className = { styles . code } >
242+ uv tool install dstack -U
243+ </ Code >
244+
245+ < div className = { styles . copy } >
246+ < Popover
247+ dismissButton = { false }
248+ position = "top"
249+ size = "small"
250+ triggerType = "custom"
251+ content = {
252+ < StatusIndicator type = "success" >
253+ { t ( 'common.copied' ) }
254+ </ StatusIndicator >
255+ }
256+ >
257+ < Button
258+ formAction = "none"
259+ iconName = "copy"
260+ variant = "normal"
261+ onClick = { ( ) =>
262+ copyToClipboard (
263+ 'uv tool install dstack -U' ,
264+ )
265+ }
266+ />
267+ </ Popover >
268+ </ div >
269+ </ div >
270+ </ >
271+ ) ,
272+ } ,
273+ {
274+ label : 'pip' ,
275+ id : 'pip' ,
276+ content : (
277+ < >
278+ < div className = { styles . codeWrapper } >
279+ < Code className = { styles . code } >
280+ pip install dstack -U
281+ </ Code >
282+
283+ < div className = { styles . copy } >
284+ < Popover
285+ dismissButton = { false }
286+ position = "top"
287+ size = "small"
288+ triggerType = "custom"
289+ content = {
290+ < StatusIndicator type = "success" >
291+ { t ( 'common.copied' ) }
292+ </ StatusIndicator >
293+ }
294+ >
295+ < Button
296+ formAction = "none"
297+ iconName = "copy"
298+ variant = "normal"
299+ onClick = { ( ) =>
300+ copyToClipboard ( 'pip install dstack -U' )
301+ }
302+ />
303+ </ Popover >
304+ </ div >
305+ </ div >
306+ </ >
307+ ) ,
308+ } ,
309+ ] }
211310 />
212- </ Popover >
213- </ div >
214- </ Hotspot >
215- </ div >
216- </ SpaceBetween >
217- </ Container >
311+ </ Hotspot >
312+ ) ,
313+ isOptional : true ,
314+ } ,
315+ {
316+ title : 'Add project' ,
317+ // info: <InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />,
318+ description : 'To use dstack with this project, run the following command.' ,
319+ content : (
320+ < div className = { styles . codeWrapper } >
321+ < Hotspot hotspotId = { HotspotIds . CONFIGURE_CLI_COMMAND } >
322+ < Code className = { styles . code } > { configCliCommand } </ Code >
323+
324+ < div className = { styles . copy } >
325+ < Popover
326+ dismissButton = { false }
327+ position = "top"
328+ size = "small"
329+ triggerType = "custom"
330+ content = {
331+ < StatusIndicator type = "success" >
332+ { t ( 'common.copied' ) }
333+ </ StatusIndicator >
334+ }
335+ >
336+ < Button
337+ formAction = "none"
338+ iconName = "copy"
339+ variant = "normal"
340+ onClick = { copyCliCommand }
341+ />
342+ </ Popover >
343+ </ div >
344+ </ Hotspot >
345+ </ div >
346+ ) ,
347+ isOptional : true ,
348+ } ,
349+ ] }
350+ />
351+ </ ExpandableSection >
218352 ) }
219353
220354 < BackendsTable
0 commit comments