Skip to content

Commit e2b6122

Browse files
committed
implemented was using new api for checking fleets
1 parent 0b6040f commit e2b6122

File tree

5 files changed

+73
-31
lines changed

5 files changed

+73
-31
lines changed
Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
import { useEffect, useMemo, useState } from 'react';
1+
import { useMemo } from 'react';
22

3-
import { useLazyGetFleetsQuery } from '../services/fleet';
43
import { useGetProjectsQuery } from '../services/project';
54

65
type Args = { projectNames?: IProject['project_name'][] };
76

87
export const useCheckingForFleetsInProjects = ({ projectNames }: Args) => {
9-
const [projectFleetMap, setProjectFleetMap] = useState<Record<IProject['project_name'], boolean>>({});
108
const { data: projectsData } = useGetProjectsQuery(undefined, {
119
skip: !!projectNames?.length,
1210
});
1311

14-
const [getFleets] = useLazyGetFleetsQuery();
12+
const { data: noFleetsProjectsData } = useGetProjectsQuery(
13+
{
14+
only_no_fleets: true,
15+
},
16+
{
17+
skip: !!projectNames?.length,
18+
},
19+
);
1520

1621
const projectNameForChecking = useMemo<IProject['project_name'][]>(() => {
1722
if (projectNames) {
@@ -25,27 +30,15 @@ export const useCheckingForFleetsInProjects = ({ projectNames }: Args) => {
2530
return [];
2631
}, [projectNames, projectsData]);
2732

28-
useEffect(() => {
29-
const fetchFleets = async () => {
30-
const map: Record<IProject['project_name'], boolean> = {};
31-
32-
await Promise.all(
33-
projectNameForChecking.map((projectName) =>
34-
getFleets({
35-
limit: 1,
36-
project_name: projectName,
37-
only_active: true,
38-
})
39-
.unwrap()
40-
.then((data) => (map[projectName] = Boolean(data.length))),
41-
),
42-
);
43-
44-
setProjectFleetMap(map);
45-
};
46-
47-
fetchFleets();
48-
}, [projectNameForChecking]);
49-
50-
return projectFleetMap;
33+
const projectHavingFleetMap = useMemo<Record<IProject['project_name'], boolean>>(() => {
34+
const map: Record<IProject['project_name'], boolean> = {};
35+
36+
projectNameForChecking.forEach((projectName) => {
37+
map[projectName] = !noFleetsProjectsData?.some((i) => i.project_name === projectName);
38+
});
39+
40+
return map;
41+
}, [projectNameForChecking, noFleetsProjectsData]);
42+
43+
return projectHavingFleetMap;
5144
};

frontend/src/pages/Runs/CreateDevEnvironment/index.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ import { useNavigate, useSearchParams } from 'react-router-dom';
55
import cn from 'classnames';
66
import * as yup from 'yup';
77
import { Box, Link, WizardProps } from '@cloudscape-design/components';
8+
import { ButtonProps } from '@cloudscape-design/components/button';
89
import { CardsProps } from '@cloudscape-design/components/cards';
910

10-
import type { TabsProps, ToggleProps } from 'components';
11+
import { Alert, Button, TabsProps, ToggleProps } from 'components';
1112
import { Container, FormCodeEditor, FormField, FormInput, FormSelect, SpaceBetween, Tabs, Toggle, Wizard } from 'components';
1213

1314
import { useBreadcrumbs, useNotifications } from 'hooks';
14-
import { getServerError } from 'libs';
15+
import { getServerError, goToUrl } from 'libs';
1516
import { ROUTES } from 'routes';
1617
import { useApplyRunMutation } from 'services/run';
1718

1819
import { OfferList } from 'pages/Offers/List';
1920

2021
import { useGenerateYaml } from './hooks/useGenerateYaml';
2122
import { useGetRunSpecFromYaml } from './hooks/useGetRunSpecFromYaml';
23+
import { useCheckingForFleetsInProjects } from '../../../hooks/useCheckingForFleetsInProjectsOfMember';
2224
import { FORM_FIELD_NAMES } from './constants';
2325

2426
import { IRunEnvironmentFormKeys, IRunEnvironmentFormValues } from './types';
@@ -117,6 +119,9 @@ export const CreateDevEnvironment: React.FC = () => {
117119

118120
const [getRunSpecFromYaml] = useGetRunSpecFromYaml({ projectName: selectedProject ?? '' });
119121

122+
const projectHavingFleetMap = useCheckingForFleetsInProjects({ projectNames: selectedProject ? [selectedProject] : [] });
123+
const projectDontHasFleets = !!selectedProject && !projectHavingFleetMap[selectedProject];
124+
120125
const [applyRun, { isLoading: isApplying }] = useApplyRunMutation();
121126

122127
const loading = isApplying;
@@ -174,6 +179,10 @@ export const CreateDevEnvironment: React.FC = () => {
174179
const stepValidators = [validateOffer, validateSecondStep, validateConfig];
175180

176181
if (reason === 'next') {
182+
if (projectDontHasFleets) {
183+
window.scrollTo(0, 0);
184+
}
185+
177186
stepValidators[activeStepIndex]?.().then((isValid) => {
178187
if (isValid) {
179188
setActiveStepIndex(requestedStepIndex);
@@ -275,8 +284,29 @@ export const CreateDevEnvironment: React.FC = () => {
275284
setValue('config_yaml', yaml);
276285
}, [yaml]);
277286

287+
const onCreateAFleet: ButtonProps['onClick'] = (event) => {
288+
event.preventDefault();
289+
goToUrl('https://dstack.ai/docs/quickstart/#create-a-fleet', true);
290+
};
291+
278292
return (
279293
<form className={cn({ [styles.wizardForm]: activeStepIndex === 0 })} onSubmit={handleSubmit(onSubmit)}>
294+
{projectDontHasFleets && (
295+
<div className={styles.alertBox}>
296+
<Alert
297+
header={t('fleets.no_alert.title')}
298+
type="info"
299+
action={
300+
<Button iconName="external" formAction="none" onClick={onCreateAFleet}>
301+
{t('fleets.no_alert.button_title')}
302+
</Button>
303+
}
304+
>
305+
The project <code>{selectedProject}</code> has no fleets. Create one before submitting a run.
306+
</Alert>
307+
</div>
308+
)}
309+
280310
<Wizard
281311
activeStepIndex={activeStepIndex}
282312
onNavigate={onNavigateHandler}

frontend/src/pages/Runs/CreateDevEnvironment/styles.module.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
@use '@cloudscape-design/design-tokens/index' as awsui;
22

3+
.alertBox {
4+
margin: 12px 0 0;
5+
6+
:global {
7+
& [class^="awsui_alert"] {
8+
& [class^="awsui_action-slot"] {
9+
display: flex;
10+
align-items: center;
11+
}
12+
}
13+
}
14+
}
15+
316
.wizardForm {
417
& [class^="awsui_wizard"] {
518
& [class^="awsui_footer"] {

frontend/src/services/project.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ export const projectApi = createApi({
2323
tagTypes: ['Projects', 'ProjectRepos', 'ProjectLogs', 'Backends'],
2424

2525
endpoints: (builder) => ({
26-
getProjects: builder.query<IProject[], void>({
27-
query: () => {
26+
getProjects: builder.query<IProject[], TGetProjectsParams | void>({
27+
query: (body) => {
2828
return {
2929
url: API.PROJECTS.LIST(),
3030
method: 'POST',
31+
body,
3132
};
3233
},
3334

frontend/src/types/project.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ declare type TCreateWizardProjectParams = {
77
};
88
};
99

10+
declare type TGetProjectsParams = {
11+
only_no_fleets?: boolean;
12+
include_not_joined?: boolean;
13+
};
14+
1015
declare type TProjectBackend = {
1116
name: string;
1217
config: IBackendAWS | IBackendAzure | IBackendGCP | IBackendLambda | IBackendLocal | IBackendDstack;

0 commit comments

Comments
 (0)