diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 22f7dd3cb..27645fa77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,6 @@ repos: hooks: - id: frontend-pre-commit name: frontend-pre-commit - entry: bash -c "cd frontend && npm install && npm run precommit" + entry: bash -c "cd frontend && npm install && npm run pre-commit" language: system pass_filenames: false diff --git a/frontend/package.json b/frontend/package.json index fa5c511ca..615922889 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,7 @@ "test": "jest", "test:update-snapshots": "jest -u", "generate-api": "npx @rtk-query/codegen-openapi openapi-config.ts", - "precommit": "lint-staged" + "pre-commit": "lint-staged" }, "devDependencies": { "@babel/cli": "^7.25.9", diff --git a/frontend/src/layouts/AppLayout/TutorialPanel/constants.tsx b/frontend/src/layouts/AppLayout/TutorialPanel/constants.tsx index 5292e24d6..7d4bb378b 100644 --- a/frontend/src/layouts/AppLayout/TutorialPanel/constants.tsx +++ b/frontend/src/layouts/AppLayout/TutorialPanel/constants.tsx @@ -43,6 +43,7 @@ export const overlayI18nStrings: AnnotationContextProps.I18nStrings = { export enum HotspotIds { ADD_TOP_UP_BALANCE = 'billing-top-up-balance', PAYMENT_CONTINUE_BUTTON = 'billing-payment-continue-button', + INSTALL_CLI_COMMAND = 'install-cli-command', CONFIGURE_CLI_COMMAND = 'configure-cli-command', CREATE_FIRST_PROJECT = 'create-first-project', } @@ -80,6 +81,7 @@ export const BILLING_TUTORIAL: TutorialPanelProps.Tutorial = { export const CONFIGURE_CLI_TUTORIAL: TutorialPanelProps.Tutorial = { completed: false, title: 'Set up the CLI', + prerequisitesAlert: 'Please, create a project before set up the CLI', description: ( <> @@ -92,6 +94,11 @@ export const CONFIGURE_CLI_TUTORIAL: TutorialPanelProps.Tutorial = { { title: 'Configure the CLI', steps: [ + { + title: 'Run the CLI install command', + content: 'Run this command on your local machine to install the CLI.', + hotspotId: HotspotIds.INSTALL_CLI_COMMAND, + }, { title: 'Run the dstack project add command', content: 'Run this command on your local machine to configure the dstack CLI.', diff --git a/frontend/src/layouts/AppLayout/TutorialPanel/hooks.ts b/frontend/src/layouts/AppLayout/TutorialPanel/hooks.ts index f27575f26..d3a465fcb 100644 --- a/frontend/src/layouts/AppLayout/TutorialPanel/hooks.ts +++ b/frontend/src/layouts/AppLayout/TutorialPanel/hooks.ts @@ -113,7 +113,11 @@ export const useTutorials = () => { dispatch(updateTutorialPanelState({ createProjectCompleted: true })); }, []); - const startConfigCliTutorial = useCallback(() => {}, [billingUrl]); + const startConfigCliTutorial = useCallback(() => { + if (projectData?.length) { + navigate(ROUTES.PROJECT.DETAILS.SETTINGS.FORMAT(projectData[0].project_name)); + } + }, [projectData]); const finishConfigCliTutorial = useCallback(() => { dispatch(updateTutorialPanelState({ configureCLICompleted: true })); @@ -160,6 +164,7 @@ export const useTutorials = () => { completed: configureCLICompleted, startCallback: startConfigCliTutorial, finishCallback: finishConfigCliTutorial, + prerequisitesNeeded: !createProjectCompleted, }, { diff --git a/frontend/src/pages/Project/Details/Settings/constants.tsx b/frontend/src/pages/Project/Details/Settings/constants.tsx index 4b3acefce..426dcb44d 100644 --- a/frontend/src/pages/Project/Details/Settings/constants.tsx +++ b/frontend/src/pages/Project/Details/Settings/constants.tsx @@ -13,7 +13,7 @@ export const CLI_INFO = {

To learn how to install the CLI, refer to the{' '} - + installation {' '} guide. diff --git a/frontend/src/pages/Project/Details/Settings/index.tsx b/frontend/src/pages/Project/Details/Settings/index.tsx index 036681885..a73a6fef7 100644 --- a/frontend/src/pages/Project/Details/Settings/index.tsx +++ b/frontend/src/pages/Project/Details/Settings/index.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; import { debounce } from 'lodash'; +import { ExpandableSection, Tabs } from '@cloudscape-design/components'; +import Wizard from '@cloudscape-design/components/wizard'; import { Box, @@ -11,7 +13,6 @@ import { Container, Header, Hotspot, - InfoLink, Loader, Popover, SelectCSD, @@ -20,10 +21,12 @@ import { } from 'components'; import { HotspotIds } from 'layouts/AppLayout/TutorialPanel/constants'; -import { useBreadcrumbs, useHelpPanel, useNotifications } from 'hooks'; +import { useBreadcrumbs, useNotifications } from 'hooks'; import { riseRouterException } from 'libs'; +import { copyToClipboard } from 'libs'; import { ROUTES } from 'routes'; import { useGetProjectQuery, useUpdateProjectMembersMutation, useUpdateProjectMutation } from 'services/project'; +import { useGetRunsQuery } from 'services/run'; import { useGetUserDataQuery } from 'services/user'; import { useCheckAvailableProjectPermission } from 'pages/Project/hooks/useCheckAvailableProjectPermission'; @@ -37,7 +40,6 @@ import { BackendsTable } from '../../Backends/Table'; import { GatewaysTable } from '../../Gateways'; import { useGatewaysTable } from '../../Gateways/hooks'; import { ProjectSecrets } from '../../Secrets'; -import { CLI_INFO } from './constants'; import styles from './styles.module.scss'; @@ -46,7 +48,7 @@ export const ProjectSettings: React.FC = () => { const params = useParams(); const navigate = useNavigate(); const paramProjectName = params.projectName ?? ''; - const [openHelpPanel] = useHelpPanel(); + const [isExpandedCliSection, setIsExpandedCliSection] = React.useState(false); const [configCliCommand, copyCliCommand] = useConfigProjectCliCommand({ projectName: paramProjectName }); const { isAvailableDeletingPermission, isProjectManager, isProjectAdmin, isAvailableProjectManaging } = @@ -60,6 +62,15 @@ export const ProjectSettings: React.FC = () => { const { data, isLoading, error } = useGetProjectQuery({ name: paramProjectName }); + const { data: runsData } = useGetRunsQuery({ + project_name: paramProjectName, + limit: 1, + }); + + useEffect(() => { + setIsExpandedCliSection(!runsData || runsData.length === 0); + }, [runsData]); + useEffect(() => { if (error && 'status' in error && error.status === 404) { riseRouterException(); @@ -167,6 +178,8 @@ export const ProjectSettings: React.FC = () => { }); }; + const [activeStepIndex, setActiveStepIndex] = React.useState(0); + if (isLoadingPage) return ( @@ -179,42 +192,163 @@ export const ProjectSettings: React.FC = () => { {data && backendsData && gatewaysData && ( {isProjectMember && ( - openHelpPanel(CLI_INFO)} />}> - {t('projects.edit.cli')} - + setIsExpandedCliSection(detail.expanded)} + headerActions={ +