diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 445c61daa9..65e33fd915 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,7 +12,7 @@ "@cloudscape-design/chat-components": "^1.0.62", "@cloudscape-design/collection-hooks": "^1.0.74", "@cloudscape-design/component-toolkit": "^1.0.0-beta.120", - "@cloudscape-design/components": "^3.0.1091", + "@cloudscape-design/components": "^3.0.1188", "@cloudscape-design/design-tokens": "^3.0.60", "@cloudscape-design/global-styles": "^1.0.45", "@hookform/resolvers": "^2.9.10", @@ -2137,9 +2137,9 @@ } }, "node_modules/@cloudscape-design/components": { - "version": "3.0.1091", - "resolved": "https://registry.npmjs.org/@cloudscape-design/components/-/components-3.0.1091.tgz", - "integrity": "sha512-ESV83m/laX9OkuITjeucYRBi4WQSu9w8yniRZjRapiTH+zTlBxQv8Gcnvr9UYPo3cbYyig2HIdbAlOagDplgfA==", + "version": "3.0.1188", + "resolved": "https://registry.npmjs.org/@cloudscape-design/components/-/components-3.0.1188.tgz", + "integrity": "sha512-Ajk7wFr2boPO7v4pgBLjdLVcA7myBiCk5hzpzURGcg+oogX9lYtXHv80k60dqyn1kzx+J0xnZwEjLa0oRmflmA==", "license": "Apache-2.0", "dependencies": { "@cloudscape-design/collection-hooks": "^1.0.0", @@ -2150,7 +2150,6 @@ "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", "ace-builds": "^1.34.0", - "balanced-match": "^1.0.2", "clsx": "^1.1.0", "d3-shape": "^1.3.7", "date-fns": "^2.25.0", @@ -6979,7 +6978,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/batch": { "version": "0.6.1", diff --git a/frontend/package.json b/frontend/package.json index f2ef0d9ca1..ae701fc95f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -101,7 +101,7 @@ "@cloudscape-design/chat-components": "^1.0.62", "@cloudscape-design/collection-hooks": "^1.0.74", "@cloudscape-design/component-toolkit": "^1.0.0-beta.120", - "@cloudscape-design/components": "^3.0.1091", + "@cloudscape-design/components": "^3.0.1188", "@cloudscape-design/design-tokens": "^3.0.60", "@cloudscape-design/global-styles": "^1.0.45", "@hookform/resolvers": "^2.9.10", diff --git a/frontend/src/components/NavigateLink/index.tsx b/frontend/src/components/NavigateLink/index.tsx index eabf4002b5..7bac639685 100644 --- a/frontend/src/components/NavigateLink/index.tsx +++ b/frontend/src/components/NavigateLink/index.tsx @@ -1,6 +1,9 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; import Link, { LinkProps } from '@cloudscape-design/components/link'; + +import styles from './style.module.scss'; + export const NavigateLink: React.FC = ({ onFollow, ...props }) => { const navigate = useNavigate(); const onFollowHandler: LinkProps['onFollow'] = (event) => { @@ -10,5 +13,9 @@ export const NavigateLink: React.FC = ({ onFollow, ...props }) => { if (event.detail.href) navigate(event.detail.href); }; - return ; + return ( + + + + ); }; diff --git a/frontend/src/components/NavigateLink/style.module.scss b/frontend/src/components/NavigateLink/style.module.scss new file mode 100644 index 0000000000..27eca2e170 --- /dev/null +++ b/frontend/src/components/NavigateLink/style.module.scss @@ -0,0 +1,5 @@ +.link { + & > a { + text-decoration: underline !important; + } +} diff --git a/frontend/src/libs/run.ts b/frontend/src/libs/run.ts index b1a626bf82..8dd8be3af6 100644 --- a/frontend/src/libs/run.ts +++ b/frontend/src/libs/run.ts @@ -4,6 +4,7 @@ import { StatusIndicatorProps } from '@cloudscape-design/components'; import { capitalize } from 'libs'; import { finishedRunStatuses } from '../pages/Runs/constants'; +import { getJobProbesStatuses } from '../pages/Runs/Details/Jobs/List/helpers'; import { IModelExtended } from '../pages/Models/List/types'; @@ -75,6 +76,16 @@ export const getRunError = (run: IRun): string | null => { return error ? capitalize(error) : null; }; +export const getRunProbe = (run: IRun): string | null => { + const job = run.jobs[0]; + + if (!job) { + return '-'; + } + + return getJobProbesStatuses(run.jobs[0]); +}; + export const getRunPriority = (run: IRun): number | null => { return run.run_spec.configuration?.priority ?? null; }; diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 342ce0cdd7..e8388f1602 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -402,6 +402,7 @@ "priority": "Priority", "provider_name": "Provider", "status": "Status", + "probe": "Probes", "submitted_at": "Submitted", "finished_at": "Finished", "metrics": { @@ -477,6 +478,7 @@ "offer": "Offer", "offer_description": "Select an offer for the dev environment.", "name": "Name", + "name_description": "The name of the run, e.g. 'my-dev-env'", "name_constraint": "Example: 'my-fleet' or 'default'. If not specified, generated automatically.", "name_placeholder": "Optional", "ide": "IDE", diff --git a/frontend/src/pages/Runs/Details/Jobs/List/helpers.ts b/frontend/src/pages/Runs/Details/Jobs/List/helpers.ts index 71247dce6a..cb5ef5a468 100644 --- a/frontend/src/pages/Runs/Details/Jobs/List/helpers.ts +++ b/frontend/src/pages/Runs/Details/Jobs/List/helpers.ts @@ -1,4 +1,5 @@ import { format } from 'date-fns'; +import type { StatusIndicatorProps } from '@cloudscape-design/components/status-indicator'; import { DATE_TIME_FORMAT } from 'consts'; import { capitalize } from 'libs'; @@ -48,6 +49,28 @@ export const getJobStatus = (job: IJob) => { return job.job_submissions?.[job.job_submissions.length - 1].status; }; +export const getJobSubmissionProbes = (job: IJob) => { + return job.job_submissions?.[job.job_submissions.length - 1].probes; +}; + +export const getJobProbesStatuses = (job: IJob): StatusIndicatorProps.Type[] => { + const status = getJobStatus(job); + const probes = getJobSubmissionProbes(job); + + if (!probes?.length || status !== 'running') { + return []; + } + + return probes.map((probe, index) => { + if (job.job_spec?.probes?.[index] && probe.success_streak >= job.job_spec.probes[index].ready_after) { + return 'success'; + } else if (probe.success_streak > 0) { + return 'in-progress'; + } + return 'not-started'; + }); +}; + export const getJobTerminationReason = (job: IJob) => { return job.job_submissions?.[job.job_submissions.length - 1].termination_reason ?? '-'; }; diff --git a/frontend/src/pages/Runs/Details/Jobs/List/hooks.tsx b/frontend/src/pages/Runs/Details/Jobs/List/hooks.tsx index a5fabd5a2f..2d89cdcaef 100644 --- a/frontend/src/pages/Runs/Details/Jobs/List/hooks.tsx +++ b/frontend/src/pages/Runs/Details/Jobs/List/hooks.tsx @@ -15,6 +15,7 @@ import { getJobListItemRegion, getJobListItemResources, getJobListItemSpot, + getJobProbesStatuses, getJobStatus, getJobStatusMessage, getJobSubmittedAt, @@ -67,6 +68,14 @@ export const useColumnsDefinitions = ({ ); }, }, + { + id: 'probe', + header: t('projects.run.probe'), + cell: (item: IJob) => { + const statuses = getJobProbesStatuses(item); + return statuses.map((statusType, index) => ); + }, + }, { id: 'priority', header: t('projects.run.priority'), diff --git a/frontend/src/pages/Runs/Details/RunDetails/index.tsx b/frontend/src/pages/Runs/Details/RunDetails/index.tsx index 24e5c2718d..5de6bb9a02 100644 --- a/frontend/src/pages/Runs/Details/RunDetails/index.tsx +++ b/frontend/src/pages/Runs/Details/RunDetails/index.tsx @@ -7,7 +7,7 @@ import { format } from 'date-fns'; import { Box, ColumnLayout, Container, Header, Loader, NavigateLink, StatusIndicator } from 'components'; import { DATE_TIME_FORMAT } from 'consts'; -import { getRunError, getRunPriority, getRunStatusMessage, getStatusIconColor, getStatusIconType } from 'libs/run'; +import { getRunError, getRunPriority, getRunProbe, getRunStatusMessage, getStatusIconColor, getStatusIconType } from 'libs/run'; import { ROUTES } from 'routes'; import { useGetRunQuery } from 'services/run'; @@ -122,6 +122,13 @@ export const RunDetails = () => { + {runData.jobs.length <= 1 && ( +
+ {t('projects.run.probe')} +
{getRunProbe(runData)}
+
+ )} +
{t('projects.run.error')}
{getRunError(runData) ?? '-'}
diff --git a/frontend/src/types/run.d.ts b/frontend/src/types/run.d.ts index 452d3a9f41..2e17206a05 100644 --- a/frontend/src/types/run.d.ts +++ b/frontend/src/types/run.d.ts @@ -171,6 +171,17 @@ declare interface IAppSpec { url_query_params?: { [key: string]: string }; } +declare interface IJobProbe { + type: 'http'; + url: string; + method?: 'head' | 'post' | 'put' | 'patch' | 'delete' | 'get'; + headers?: Array<{ name: string; value: string }>; + body?: string; + timeout: number; + interval: number; + ready_after: number; +} + declare interface IJobSpec { app_specs?: IAppSpec; commands: string[]; @@ -181,6 +192,7 @@ declare interface IJobSpec { job_num: number; max_duration?: number; working_dir: string; + probes?: IJobProbe[]; } declare interface IGpu { @@ -234,6 +246,7 @@ declare interface IJobSubmission { exit_status?: number | null; status_message?: string | null; error?: string | null; + probes?: Array<{ success_streak: number }>; } declare interface IJob {