Skip to content

Commit 6e14111

Browse files
committed
[UX] Improved UX of the project settings CLI section
1 parent 0ce40e3 commit 6e14111

File tree

3 files changed

+105
-86
lines changed

3 files changed

+105
-86
lines changed

frontend/src/layouts/AppLayout/TutorialPanel/constants.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const overlayI18nStrings: AnnotationContextProps.I18nStrings = {
4343
export enum HotspotIds {
4444
ADD_TOP_UP_BALANCE = 'billing-top-up-balance',
4545
PAYMENT_CONTINUE_BUTTON = 'billing-payment-continue-button',
46+
INSTALL_CLI_COMMAND = 'install-cli-command',
4647
CONFIGURE_CLI_COMMAND = 'configure-cli-command',
4748
CREATE_FIRST_PROJECT = 'create-first-project',
4849
}
@@ -80,6 +81,7 @@ export const BILLING_TUTORIAL: TutorialPanelProps.Tutorial = {
8081
export const CONFIGURE_CLI_TUTORIAL: TutorialPanelProps.Tutorial = {
8182
completed: false,
8283
title: 'Set up the CLI',
84+
prerequisitesAlert: 'Please, create a project before set up the CLI',
8385
description: (
8486
<>
8587
<Box variant="p" color="text-body-secondary" padding={{ top: 'n' }}>
@@ -92,6 +94,11 @@ export const CONFIGURE_CLI_TUTORIAL: TutorialPanelProps.Tutorial = {
9294
{
9395
title: 'Configure the CLI',
9496
steps: [
97+
{
98+
title: 'Run the CLI install command',
99+
content: 'Run this command on your local machine to install the CLI.',
100+
hotspotId: HotspotIds.INSTALL_CLI_COMMAND,
101+
},
95102
{
96103
title: 'Run the dstack project add command',
97104
content: 'Run this command on your local machine to configure the dstack CLI.',

frontend/src/layouts/AppLayout/TutorialPanel/hooks.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ export const useTutorials = () => {
113113
dispatch(updateTutorialPanelState({ createProjectCompleted: true }));
114114
}, []);
115115

116-
const startConfigCliTutorial = useCallback(() => {}, [billingUrl]);
116+
const startConfigCliTutorial = useCallback(() => {
117+
if (projectData?.length) {
118+
navigate(ROUTES.PROJECT.DETAILS.SETTINGS.FORMAT(projectData[0].project_name));
119+
}
120+
}, [projectData]);
117121

118122
const finishConfigCliTutorial = useCallback(() => {
119123
dispatch(updateTutorialPanelState({ configureCLICompleted: true }));
@@ -160,6 +164,7 @@ export const useTutorials = () => {
160164
completed: configureCLICompleted,
161165
startCallback: startConfigCliTutorial,
162166
finishCallback: finishConfigCliTutorial,
167+
prerequisitesNeeded: !createProjectCompleted,
163168
},
164169

165170
{

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

Lines changed: 92 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const ProjectSettings: React.FC = () => {
4848
const params = useParams();
4949
const navigate = useNavigate();
5050
const paramProjectName = params.projectName ?? '';
51+
const [isExpandedCliSection, setIsExpandedCliSection] = React.useState(false);
5152
const [configCliCommand, copyCliCommand] = useConfigProjectCliCommand({ projectName: paramProjectName });
5253

5354
const { isAvailableDeletingPermission, isProjectManager, isProjectAdmin, isAvailableProjectManaging } =
@@ -61,6 +62,15 @@ export const ProjectSettings: React.FC = () => {
6162

6263
const { data, isLoading, error } = useGetProjectQuery({ name: paramProjectName });
6364

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+
6474
useEffect(() => {
6575
if (error && 'status' in error && error.status === 404) {
6676
riseRouterException();
@@ -170,15 +180,6 @@ export const ProjectSettings: React.FC = () => {
170180

171181
const [activeStepIndex, setActiveStepIndex] = React.useState(0);
172182

173-
const { data: runsData } = useGetRunsQuery({
174-
limit: 1,
175-
});
176-
const [expanded, setExpanded] = React.useState(false);
177-
178-
useEffect(() => {
179-
setExpanded(!runsData || runsData.length === 0);
180-
}, [runsData]);
181-
182183
if (isLoadingPage)
183184
return (
184185
<Container>
@@ -194,13 +195,13 @@ export const ProjectSettings: React.FC = () => {
194195
<ExpandableSection
195196
variant="container"
196197
headerText="CLI"
197-
expanded={expanded}
198-
onChange={({ detail }) => setExpanded(detail.expanded)}
198+
expanded={isExpandedCliSection}
199+
onChange={({ detail }) => setIsExpandedCliSection(detail.expanded)}
199200
headerActions={
200201
<Button
201202
iconName="script"
202-
variant={expanded ? 'normal' : 'primary'}
203-
onClick={() => setExpanded((prev) => !prev)}
203+
variant={isExpandedCliSection ? 'normal' : 'primary'}
204+
onClick={() => setIsExpandedCliSection((prev) => !prev)}
204205
/>
205206
}
206207
// headerInfo={<InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />}
@@ -218,7 +219,7 @@ export const ProjectSettings: React.FC = () => {
218219
}}
219220
onNavigate={({ detail }) => setActiveStepIndex(detail.requestedStepIndex)}
220221
activeStepIndex={activeStepIndex}
221-
onSubmit={() => setExpanded(false)}
222+
onSubmit={() => setIsExpandedCliSection(false)}
222223
submitButtonText="Dismiss"
223224
allowSkipTo={true}
224225
steps={[
@@ -227,81 +228,87 @@ export const ProjectSettings: React.FC = () => {
227228
// info: <InfoLink onFollow={() => openHelpPanel(CLI_INFO)} />,
228229
description: 'To use dstack, install the CLI on your local machine.',
229230
content: (
230-
<Tabs
231-
variant="stacked"
232-
tabs={[
233-
{
234-
label: 'uv',
235-
id: 'uv',
236-
content: (
237-
<>
238-
<div className={styles.codeWrapper}>
239-
<Code className={styles.code}>
240-
uv tool install dstack -U
241-
</Code>
242-
243-
<div className={styles.copy}>
244-
<Popover
245-
dismissButton={false}
246-
position="top"
247-
size="small"
248-
triggerType="custom"
249-
content={
250-
<StatusIndicator type="success">
251-
{t('common.copied')}
252-
</StatusIndicator>
253-
}
254-
>
255-
<Button
256-
formAction="none"
257-
iconName="copy"
258-
variant="normal"
259-
onClick={() =>
260-
copyToClipboard('uv tool install dstack -U')
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>
261255
}
262-
/>
263-
</Popover>
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>
264269
</div>
265-
</div>
266-
</>
267-
),
268-
},
269-
{
270-
label: 'pip',
271-
id: 'pip',
272-
content: (
273-
<>
274-
<div className={styles.codeWrapper}>
275-
<Code className={styles.code}>pip install dstack -U</Code>
276-
277-
<div className={styles.copy}>
278-
<Popover
279-
dismissButton={false}
280-
position="top"
281-
size="small"
282-
triggerType="custom"
283-
content={
284-
<StatusIndicator type="success">
285-
{t('common.copied')}
286-
</StatusIndicator>
287-
}
288-
>
289-
<Button
290-
formAction="none"
291-
iconName="copy"
292-
variant="normal"
293-
onClick={() =>
294-
copyToClipboard('pip install dstack -U')
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>
295293
}
296-
/>
297-
</Popover>
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>
298305
</div>
299-
</div>
300-
</>
301-
),
302-
},
303-
]}
304-
/>
306+
</>
307+
),
308+
},
309+
]}
310+
/>
311+
</Hotspot>
305312
),
306313
isOptional: true,
307314
},

0 commit comments

Comments
 (0)