Skip to content

Commit 3f71f97

Browse files
[UX] Improved UX of the project settings CLI section
1 parent 65f8d48 commit 3f71f97

3 files changed

Lines changed: 152 additions & 42 deletions

File tree

frontend/src/pages/Project/Details/Settings/constants.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const CLI_INFO = {
1313
</p>
1414
<p>
1515
To learn how to install the CLI, refer to the{' '}
16-
<a href={'https://dstack.ai/docs/cli/installation'} target="_blank">
16+
<a href={'https://dstack.ai/docs/installation#set-up-the-cli'} target="_blank">
1717
installation
1818
</a>{' '}
1919
guide.

frontend/src/pages/Project/Details/Settings/index.tsx

Lines changed: 150 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { useNavigate, useParams } from 'react-router-dom';
44
import { debounce } from 'lodash';
5+
import Wizard from "@cloudscape-design/components/wizard";
56

67
import {
78
Box,
@@ -40,6 +41,9 @@ import { ProjectSecrets } from '../../Secrets';
4041
import { CLI_INFO } from './constants';
4142

4243
import styles from './styles.module.scss';
44+
import { ExpandableSection, Tabs } from '@cloudscape-design/components';
45+
import { useGetRunsQuery } from 'services/run';
46+
import { copyToClipboard } from 'libs';
4347

4448
export const ProjectSettings: React.FC = () => {
4549
const { t } = useTranslation();
@@ -167,6 +171,21 @@ export const ProjectSettings: React.FC = () => {
167171
});
168172
};
169173

174+
const [
175+
activeStepIndex,
176+
setActiveStepIndex
177+
] = React.useState(0);
178+
179+
180+
const { data: runsData } = useGetRunsQuery({
181+
limit: 1,
182+
});
183+
const [expanded, setExpanded] = React.useState(false);
184+
185+
useEffect(() => {
186+
setExpanded(!runsData || runsData.length === 0);
187+
}, [runsData]);
188+
170189
if (isLoadingPage)
171190
return (
172191
<Container>
@@ -179,53 +198,144 @@ export const ProjectSettings: React.FC = () => {
179198
{data && backendsData && gatewaysData && (
180199
<SpaceBetween size="l">
181200
{isProjectMember && (
182-
<Container
183-
header={
184-
<Header variant="h2" info={<InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />}>
185-
{t('projects.edit.cli')}
186-
</Header>
187-
}
201+
<ExpandableSection
202+
variant="container"
203+
headerText="CLI"
204+
expanded={expanded}
205+
onChange={({ detail }) => setExpanded(detail.expanded)}
206+
headerActions={<Button iconName='script' variant={expanded ? "normal" : "primary"} onClick={() => setExpanded((prev) => !prev)}></Button>}
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}
211-
/>
212-
</Popover>
213-
</div>
214-
</Hotspot>
215-
</div>
216-
</SpaceBetween>
217-
</Container>
209+
<Wizard
210+
i18nStrings={{
211+
stepNumberLabel: stepNumber =>
212+
`Step ${stepNumber}`,
213+
collapsedStepsLabel: (stepNumber, stepsCount) =>
214+
`Step ${stepNumber} of ${stepsCount}`,
215+
skipToButtonLabel: (step, stepNumber) =>
216+
`Skip to ${step.title}`,
217+
navigationAriaLabel: "Steps",
218+
// cancelButton: "Cancel",
219+
previousButton: "Previous",
220+
nextButton: "Next",
221+
optional: "required"
222+
}}
223+
onNavigate={({ detail }) =>
224+
setActiveStepIndex(detail.requestedStepIndex)
225+
}
226+
activeStepIndex={activeStepIndex}
227+
onSubmit={() => setExpanded(false)}
228+
submitButtonText="Dismiss"
229+
allowSkipTo={true}
230+
steps={[
231+
{
232+
title: "Install CLI",
233+
// info: <InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />,
234+
description: "To use dstack, install the CLI on your local machine.",
235+
content: (
236+
<Tabs
237+
variant="stacked"
238+
tabs={[
239+
{
240+
label: "uv",
241+
id: "uv",
242+
content: <>
243+
<div className={styles.codeWrapper}>
244+
<Code className={styles.code}>uv tool install dstack -U</Code>
245+
246+
<div className={styles.copy}>
247+
<Popover
248+
dismissButton={false}
249+
position="top"
250+
size="small"
251+
triggerType="custom"
252+
content={<StatusIndicator type="success">{t('common.copied')}</StatusIndicator>}
253+
>
254+
<Button
255+
formAction="none"
256+
iconName="copy"
257+
variant="normal"
258+
onClick={() => copyToClipboard('uv tool install dstack -U')}
259+
/>
260+
</Popover>
261+
</div>
262+
</div>
263+
</>
264+
},
265+
{
266+
label: "pip",
267+
id: "pip",
268+
content: <>
269+
<div className={styles.codeWrapper}>
270+
<Code className={styles.code}>pip install dstack -U</Code>
271+
272+
<div className={styles.copy}>
273+
<Popover
274+
dismissButton={false}
275+
position="top"
276+
size="small"
277+
triggerType="custom"
278+
content={<StatusIndicator type="success">{t('common.copied')}</StatusIndicator>}
279+
>
280+
<Button
281+
formAction="none"
282+
iconName="copy"
283+
variant="normal"
284+
onClick={() => copyToClipboard('pip install dstack -U')}
285+
/>
286+
</Popover>
287+
</div>
288+
</div>
289+
</>
290+
}]}
291+
/>
292+
),
293+
isOptional: true,
294+
},
295+
{
296+
title: "Add project",
297+
// info: <InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />,
298+
description: "To use dstack with this project, run the following command.",
299+
content: (
300+
<div className={styles.codeWrapper}>
301+
<Hotspot hotspotId={HotspotIds.CONFIGURE_CLI_COMMAND}>
302+
<Code className={styles.code}>{configCliCommand}</Code>
303+
304+
<div className={styles.copy}>
305+
<Popover
306+
dismissButton={false}
307+
position="top"
308+
size="small"
309+
triggerType="custom"
310+
content={<StatusIndicator type="success">{t('common.copied')}</StatusIndicator>}
311+
>
312+
<Button
313+
formAction="none"
314+
iconName="copy"
315+
variant="normal"
316+
onClick={copyCliCommand}
317+
/>
318+
</Popover>
319+
</div>
320+
</Hotspot>
321+
</div>
322+
),
323+
isOptional: true,
324+
},
325+
]}
326+
/>
327+
</ExpandableSection>
218328
)}
219329

220330
<BackendsTable
221331
backends={backendsData}
222332
{...(isProjectManager(data)
223333
? {
224-
onClickAddBackend: addBackend,
225-
editBackend: editBackend,
226-
deleteBackends: deleteBackend,
227-
isDisabledDelete: isDeletingBackend,
228-
}
334+
onClickAddBackend: addBackend,
335+
editBackend: editBackend,
336+
deleteBackends: deleteBackend,
337+
isDisabledDelete: isDeletingBackend,
338+
}
229339
: {})}
230340
/>
231341

frontend/src/pages/Project/hooks/useConfigProjectCliComand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type Args = {
99
export const useConfigProjectCliCommand = ({ projectName }: Args) => {
1010
const currentUserToken = useAppSelector(selectAuthToken);
1111

12-
const cliCommand = `dstack project add --name ${projectName} --url ${location.origin} --token ${currentUserToken}`;
12+
const cliCommand = `dstack project add \\\n --url ${location.origin} \\\n --name ${projectName} \\\n --token ${currentUserToken}`;
1313

1414
const copyCliCommand = () => {
1515
copyToClipboard(cliCommand);

0 commit comments

Comments
 (0)