Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/src/App/Login/LoginByGithubCallback/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export const LoginByGithubCallback: React.FC = () => {
}
githubCallback({ code, state })
.unwrap()
.then(async ({ creds: { token } }) => {
.then(async ({ username, creds: { token } }) => {
dispatch(setAuthData({ token }));
if (product.hasPresets) {
const result = await getProjects({}).unwrap();
const result = await getProjects({ username, limit: 1 }).unwrap();
if (result.data.length === 0) {
navigate(ROUTES.PROJECT.ADD);
return;
Expand Down
34 changes: 11 additions & 23 deletions frontend/src/hooks/useCheckingForFleetsInProjectsOfMember.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
import { useMemo } from 'react';

import { useGetOnlyNoFleetsProjectsQuery, useGetProjectsQuery } from 'services/project';
import { useGetOnlyNoFleetsProjectsQuery } from 'services/project';

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

export const useCheckingForFleetsInProjects = ({ projectNames }: Args) => {
const { data: projectsData } = useGetProjectsQuery(
{},
{
skip: !!projectNames?.length,
},
);

const { data: noFleetsProjectsData } = useGetOnlyNoFleetsProjectsQuery();

const projectNameForChecking = useMemo<IProject['project_name'][]>(() => {
if (projectNames) {
return projectNames;
}

if (projectsData?.data) {
return projectsData.data.map((project) => project.project_name);
}

return [];
}, [projectNames, projectsData]);

const projectHavingFleetMap = useMemo<Record<IProject['project_name'], boolean>>(() => {
const map: Record<IProject['project_name'], boolean> = {};

projectNameForChecking.forEach((projectName) => {
projectNames.forEach((projectName) => {
map[projectName] = !noFleetsProjectsData?.some((i) => i.project_name === projectName);
});

return map;
}, [projectNameForChecking, noFleetsProjectsData]);
}, [projectNames, noFleetsProjectsData]);

return projectHavingFleetMap;
};

export const useNewestProjectWithoutFleets = (): IProject['project_name'] | undefined => {
const { data: noFleetsProjectsData } = useGetOnlyNoFleetsProjectsQuery();

// The endpoint sorts projects by ascending creation time
return noFleetsProjectsData?.[noFleetsProjectsData.length - 1]?.project_name;
};
6 changes: 5 additions & 1 deletion frontend/src/hooks/useProjectFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { SelectCSDProps } from 'components';

import { useGetProjectsQuery } from 'services/project';

import { selectUserName } from 'App/slice';

import useAppSelector from './useAppSelector';
import { useLocalStorageState } from './useLocalStorageState';

type Args = {
Expand All @@ -16,7 +19,8 @@ export const useProjectFilter = ({ localStorePrefix }: Args) => {
null,
);

const { data: projectsData, isLoading } = useGetProjectsQuery({});
const userName = useAppSelector(selectUserName);
const { data: projectsData, isLoading } = useGetProjectsQuery({ username: userName ?? '' }, { skip: !userName });

const projectOptions = useMemo<SelectCSDProps.Options>(() => {
if (!projectsData?.data?.length) return [];
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/layouts/AppLayout/TutorialPanel/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const useTutorials = () => {
} = useAppSelector(selectTutorialPanel);

const { data: userBillingData } = useGetUserBillingInfoQuery({ username: useName ?? '' }, { skip: !useName });
const { data: projectData } = useGetProjectsQuery({});
const { data: projectData } = useGetProjectsQuery({ username: useName ?? '', limit: 1 }, { skip: !useName });
const { data: runsData } = useGetRunsQuery({
limit: 1,
job_submissions_limit: 1,
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -844,9 +844,7 @@
"balance": "Balance",
"billing_history": "Billing history",
"payment_method": "Payment method",
"no_payment_method": "No payment method attached",
"top_up_balance": "Top up balance",
"edit_payment_method": "Edit payment method",
"payment_amount": "Payment amount",
"amount_description": "Minimum: ${{value}}",
"make_payment": "Make a payment",
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/pages/Fleets/List/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Button, Header, Loader, PropertyFilter, SpaceBetween, Table, Toggle } f

import { DEFAULT_TABLE_PAGE_SIZE } from 'consts';
import { useBreadcrumbs, useCollection, useInfiniteScroll } from 'hooks';
import { useCheckingForFleetsInProjects } from 'hooks/useCheckingForFleetsInProjectsOfMember';
import { useNewestProjectWithoutFleets } from 'hooks/useCheckingForFleetsInProjectsOfMember';
import { ROUTES } from 'routes';
import { useLazyGetFleetsQuery } from 'services/fleet';

Expand Down Expand Up @@ -40,7 +40,7 @@ export const FleetList: React.FC = () => {
handleLoadItems,
} = useFilters();

const projectHavingFleetMap = useCheckingForFleetsInProjects({});
const projectDontHasFleet = useNewestProjectWithoutFleets();

const { data, isLoading, refreshList, isLoadingMore } = useInfiniteScroll<IFleet, TFleetListRequestParams>({
useLazyQuery: useLazyGetFleetsQuery,
Expand Down Expand Up @@ -74,8 +74,6 @@ export const FleetList: React.FC = () => {
deleteFleets([...selectedItems]).catch(console.log);
};

const projectDontHasFleet = Object.keys(projectHavingFleetMap).find((project) => !projectHavingFleetMap[project]);

return (
<Table
{...collectionProps}
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/pages/Runs/List/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button, Header, Loader, PropertyFilter, SpaceBetween, Table, Toggle } f

import { DEFAULT_TABLE_PAGE_SIZE } from 'consts';
import { useBreadcrumbs, useCollection, useInfiniteScroll } from 'hooks';
import { useCheckingForFleetsInProjects } from 'hooks/useCheckingForFleetsInProjectsOfMember';
import { useNewestProjectWithoutFleets } from 'hooks/useCheckingForFleetsInProjectsOfMember';
import { ROUTES } from 'routes';
import { useGetRunsQuery, useLazyGetRunsQuery } from 'services/run';

Expand Down Expand Up @@ -50,7 +50,7 @@ export const RunList: React.FC = () => {
filteringStatusType,
handleLoadItems,
} = useFilters();
const projectHavingFleetMap = useCheckingForFleetsInProjects({});
const projectDontHasFleet = useNewestProjectWithoutFleets();

const { data, isLoading, refreshList, isLoadingMore } = useInfiniteScroll<IRun, TRunsRequestParams>({
useLazyQuery: useLazyGetRunsQuery,
Expand Down Expand Up @@ -131,8 +131,6 @@ export const RunList: React.FC = () => {
);
};

const projectDontHasFleet = Object.keys(projectHavingFleetMap).find((project) => !projectHavingFleetMap[project]);

return (
<Table
{...collectionProps}
Expand Down
28 changes: 1 addition & 27 deletions frontend/src/pages/User/Details/Billing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ import { HotspotIds } from 'layouts/AppLayout/TutorialPanel/constants';
import { useAppSelector, useBreadcrumbs, useNotifications } from 'hooks';
import { centsToFormattedString, getServerError, goToUrl } from 'libs';
import { ROUTES } from 'routes';
import {
useGetUserBillingInfoQuery,
useUserBillingCheckoutSessionMutation,
// useUserBillingPortalSessionMutation,
} from 'services/user';
import { useGetUserBillingInfoQuery, useUserBillingCheckoutSessionMutation } from 'services/user';
import { GlobalUserRole } from 'types';

import { selectUserName } from 'App/slice';
Expand All @@ -37,7 +33,6 @@ export const Billing: React.FC = () => {

const { data, isLoading } = useGetUserBillingInfoQuery({ username: paramUserName });
const [billingCheckout, { isLoading: isLoadingBillingCheckout }] = useUserBillingCheckoutSessionMutation();
// const [billingPortalSession, { isLoading: isLoadingBillingPortalSession }] = useUserBillingPortalSessionMutation();

useBreadcrumbs([
{
Expand Down Expand Up @@ -81,21 +76,6 @@ export const Billing: React.FC = () => {
setShowPaymentModal(true);
};

// const editPaymentMethod = () => {
// billingPortalSession({
// username: paramUserName,
// })
// .unwrap()
// .then((data) => goToUrl(data.url))
// .catch((error) => {
// pushNotification({
// type: 'error',
// content: t('common.server_error', { error: getServerError(error) }),
// });
// })
// .finally(closeModal);
// };

const closeModal = () => {
setShowPaymentModal(false);
};
Expand All @@ -119,12 +99,6 @@ export const Billing: React.FC = () => {
</Button>
</Hotspot>
)}

{/* {data?.is_payment_method_attached && (
<Button formAction="none" onClick={editPaymentMethod} disabled={isLoadingBillingPortalSession}>
{t('billing.edit_payment_method')}
</Button>
)} */}
</SpaceBetween>
</SpaceBetween>
)}
Expand Down
1 change: 0 additions & 1 deletion frontend/src/types/user.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ declare interface IUserAuthData extends Pick<IUserWithCreds['creds'], 'token'> {

declare interface IUserBillingInfo {
balance: number;
is_payment_method_attached: boolean;
default_payment_amount: number;
billing_history: IPayment[];
}
4 changes: 4 additions & 0 deletions src/dstack/_internal/server/routers/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ async def get_run(
If given `id`, returns deleted runs.
"""
_, project = user_project
# TODO: Limit job submissions returned per job and introduce a paginated API
# to list job submissions. Every call returns the full submission history,
# which grows with retries and redeployments. Note that `Run.cost` is summed
# over the loaded submissions, so it must be computed separately first.
run = await runs.get_run(
session=session,
project=project,
Expand Down
4 changes: 4 additions & 0 deletions src/dstack/_internal/server/services/runs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,10 @@ async def _get_run_repo_or_error(


def _get_run_cost(run: Run) -> float:
# FIXME: The cost is summed only over the loaded job submissions, so it is
# underestimated when `job_submissions_limit` drops older submissions,
# e.g. in the UI run list for runs with retries or redeployments.
# Once metering tables are introduced, calculate run costs efficiently from them.
run_cost = math.fsum(
_get_job_submission_cost(submission)
for job in run.jobs
Expand Down
Loading