diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 65e33fd915..e76e6e8b13 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -30,6 +30,7 @@
"rc-tooltip": "^5.2.2",
"react": "^18.3.1",
"react-avatar": "^5.0.3",
+ "react-bus": "^4.0.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
"react-hook-form": "^7.53.0",
@@ -13904,6 +13905,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
"node_modules/mnth": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mnth/-/mnth-2.0.0.tgz",
@@ -21492,6 +21499,19 @@
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-bus": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/react-bus/-/react-bus-4.0.1.tgz",
+ "integrity": "sha512-tzPWE23WN0U9v3YaGKAlLW7GXYv1YkCUgWcnzm8HDtfBeD2vDvK8PYHkJVrwMdzg5BleHcxejtdKYfIYZxD7PQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "^18.0.8",
+ "mitt": "^3.0.1"
+ },
+ "peerDependencies": {
+ "react": ">=17.0.0 || ^19.0.0-0"
+ }
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index ae701fc95f..f4dfc7c09e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -119,6 +119,7 @@
"rc-tooltip": "^5.2.2",
"react": "^18.3.1",
"react-avatar": "^5.0.3",
+ "react-bus": "^4.0.1",
"react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
"react-hook-form": "^7.53.0",
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx
index 8820bd30d2..c02da1084c 100644
--- a/frontend/src/index.tsx
+++ b/frontend/src/index.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { Provider as BusProvider } from 'react-bus';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { RouterProvider } from 'react-router-dom';
@@ -36,7 +37,9 @@ if (container) {
root.render(
-
+
+
+
,
);
diff --git a/frontend/src/pages/Runs/Details/Events/List/index.tsx b/frontend/src/pages/Runs/Details/Events/List/index.tsx
index 79ccb54436..fb6610033e 100644
--- a/frontend/src/pages/Runs/Details/Events/List/index.tsx
+++ b/frontend/src/pages/Runs/Details/Events/List/index.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { useListener } from 'react-bus';
import { useTranslation } from 'react-i18next';
import { useNavigate, useParams } from 'react-router-dom';
import Button from '@cloudscape-design/components/button';
@@ -12,13 +13,15 @@ import { useLazyGetAllEventsQuery } from 'services/events';
import { useColumnsDefinitions } from 'pages/Events/List/hooks/useColumnDefinitions';
+import { RUN_DETAILS_REFRESH_LIST_EVENT } from '../../constants';
+
export const EventsList = () => {
const { t } = useTranslation();
const params = useParams();
const paramRunId = params.runId ?? '';
const navigate = useNavigate();
- const { data, isLoading, isLoadingMore } = useInfiniteScroll({
+ const { data, isLoading, isLoadingMore, refreshList } = useInfiniteScroll({
useLazyQuery: useLazyGetAllEventsQuery,
args: { limit: DEFAULT_TABLE_PAGE_SIZE, within_runs: [paramRunId] },
@@ -28,6 +31,8 @@ export const EventsList = () => {
}),
});
+ useListener(RUN_DETAILS_REFRESH_LIST_EVENT, refreshList);
+
const { items, collectionProps } = useCollection(data, {
selection: {},
});
diff --git a/frontend/src/pages/Runs/Details/Inspect/index.tsx b/frontend/src/pages/Runs/Details/Inspect/index.tsx
index 5dc9e9a46b..68c4b7bcbb 100644
--- a/frontend/src/pages/Runs/Details/Inspect/index.tsx
+++ b/frontend/src/pages/Runs/Details/Inspect/index.tsx
@@ -1,4 +1,5 @@
import React, { useEffect, useMemo } from 'react';
+import { useListener } from 'react-bus';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
@@ -6,6 +7,8 @@ import { CodeEditor, Container, Header, Loader } from 'components';
import { useGetRunQuery } from 'services/run';
+import { RUN_DETAILS_REFRESH_LIST_EVENT } from '../constants';
+
interface AceEditorElement extends HTMLElement {
env?: {
editor?: {
@@ -20,11 +23,17 @@ export const RunInspect = () => {
const paramProjectName = params.projectName ?? '';
const paramRunId = params.runId ?? '';
- const { data: runData, isLoading } = useGetRunQuery({
+ const {
+ data: runData,
+ isLoading,
+ refetch,
+ } = useGetRunQuery({
project_name: paramProjectName,
id: paramRunId,
});
+ useListener(RUN_DETAILS_REFRESH_LIST_EVENT, refetch);
+
const jsonContent = useMemo(() => {
if (!runData) return '';
return JSON.stringify(runData, null, 2);
diff --git a/frontend/src/pages/Runs/Details/Jobs/Metrics/index.tsx b/frontend/src/pages/Runs/Details/Jobs/Metrics/index.tsx
index dd088c418b..db89033984 100644
--- a/frontend/src/pages/Runs/Details/Jobs/Metrics/index.tsx
+++ b/frontend/src/pages/Runs/Details/Jobs/Metrics/index.tsx
@@ -1,4 +1,5 @@
import React, { useEffect, useMemo } from 'react';
+import { useListener } from 'react-bus';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
@@ -7,6 +8,7 @@ import { Box, ColumnLayout, Container, Header, LineChart } from 'components';
import { riseRouterException } from 'libs';
import { useGetRunQuery } from 'services/run';
+import { RUN_DETAILS_REFRESH_LIST_EVENT } from '../../constants';
import { bytesFormatter, formatPercent, formatTime } from './helpers';
import { useMetricsData } from './useMetricsData';
@@ -32,7 +34,14 @@ export const JobMetrics: React.FC = () => {
return runData.jobs.find((job) => job.job_spec.job_name === paramJobName) ?? null;
}, [runData]);
- const { cpuChartProps, memoryChartProps, eachGPUChartProps, eachGPUMemoryChartProps, isLoading } = useMetricsData({
+ const {
+ cpuChartProps,
+ memoryChartProps,
+ eachGPUChartProps,
+ eachGPUMemoryChartProps,
+ isLoading,
+ refetch: refetchMetrics,
+ } = useMetricsData({
project_name: paramProjectName,
run_name: runData?.run_spec.run_name ?? '',
run_id: runData?.id ?? '',
@@ -40,6 +49,8 @@ export const JobMetrics: React.FC = () => {
limit: 1000,
});
+ useListener(RUN_DETAILS_REFRESH_LIST_EVENT, refetchMetrics);
+
const statusType = isLoading || isLoadingRun ? 'loading' : 'finished';
useEffect(() => {
diff --git a/frontend/src/pages/Runs/Details/Jobs/Metrics/useMetricsData.ts b/frontend/src/pages/Runs/Details/Jobs/Metrics/useMetricsData.ts
index 758e3faa33..70bc1d6e5a 100644
--- a/frontend/src/pages/Runs/Details/Jobs/Metrics/useMetricsData.ts
+++ b/frontend/src/pages/Runs/Details/Jobs/Metrics/useMetricsData.ts
@@ -15,7 +15,11 @@ import {
import { bytesFormatter, getChartProps } from './helpers';
export const useMetricsData = (params: TJobMetricsRequestParams) => {
- const { data: metricsData, isLoading } = useGetMetricsQuery(params, {
+ const {
+ data: metricsData,
+ isLoading,
+ refetch,
+ } = useGetMetricsQuery(params, {
skip: !params.run_name,
});
@@ -76,5 +80,5 @@ export const useMetricsData = (params: TJobMetricsRequestParams) => {
});
}, [metricsData]);
- return { cpuChartProps, eachGPUChartProps, memoryChartProps, eachGPUMemoryChartProps, isLoading };
+ return { cpuChartProps, eachGPUChartProps, memoryChartProps, eachGPUMemoryChartProps, isLoading, refetch };
};
diff --git a/frontend/src/pages/Runs/Details/Logs/index.tsx b/frontend/src/pages/Runs/Details/Logs/index.tsx
index 2c836bb08d..4d12520bba 100644
--- a/frontend/src/pages/Runs/Details/Logs/index.tsx
+++ b/frontend/src/pages/Runs/Details/Logs/index.tsx
@@ -1,4 +1,5 @@
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
+import { useListener } from 'react-bus';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import classNames from 'classnames';
@@ -10,6 +11,7 @@ import { useLazyGetProjectLogsQuery } from 'services/project';
import { useGetRunQuery } from 'services/run';
import { LogRow } from './components/LogRow';
+import { RUN_DETAILS_REFRESH_LIST_EVENT } from '../constants';
import { decodeLogs, getJobSubmissionId } from './helpers';
import { IProps } from './types';
@@ -116,6 +118,10 @@ export const Logs: React.FC = ({ className, projectName, runName, jobSub
getLogItems();
}, []);
+ const refreshLogs = useCallback(() => getLogItems(), []);
+
+ useListener(RUN_DETAILS_REFRESH_LIST_EVENT, refreshLogs);
+
useLayoutEffect(() => {
if (logsForView.length && logsForView.length <= LIMIT_LOG_ROWS) {
scrollToBottom();
diff --git a/frontend/src/pages/Runs/Details/RunDetails/index.tsx b/frontend/src/pages/Runs/Details/RunDetails/index.tsx
index 408d4cb16b..9a34b6af74 100644
--- a/frontend/src/pages/Runs/Details/RunDetails/index.tsx
+++ b/frontend/src/pages/Runs/Details/RunDetails/index.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { useListener } from 'react-bus';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { format } from 'date-fns';
@@ -28,9 +29,9 @@ import {
getRunListItemRegion,
getRunListItemResources,
getRunListItemSchedule,
- getRunListItemServiceUrl,
getRunListItemSpotLabelKey,
} from '../../List/helpers';
+import { RUN_DETAILS_REFRESH_LIST_EVENT } from '../constants';
import { EventsList } from '../Events/List';
import { JobList } from '../Jobs/List';
import { ConnectToRunWithDevEnvConfiguration } from './ConnectToRunWithDevEnvConfiguration';
@@ -42,11 +43,17 @@ export const RunDetails = () => {
const paramProjectName = params.projectName ?? '';
const paramRunId = params.runId ?? '';
- const { data: runData, isLoading: isLoadingRun } = useGetRunQuery({
+ const {
+ data: runData,
+ isLoading: isLoadingRun,
+ refetch,
+ } = useGetRunQuery({
project_name: paramProjectName,
id: paramRunId,
});
+ useListener(RUN_DETAILS_REFRESH_LIST_EVENT, refetch);
+
const schedule = runData ? getRunListItemSchedule(runData) : null;
const nextTriggeredAt = runData ? runData.next_triggered_at : null;
diff --git a/frontend/src/pages/Runs/Details/constants.ts b/frontend/src/pages/Runs/Details/constants.ts
index 7a63d3f95c..be8b1da878 100644
--- a/frontend/src/pages/Runs/Details/constants.ts
+++ b/frontend/src/pages/Runs/Details/constants.ts
@@ -5,3 +5,5 @@ export enum CodeTab {
Events = 'events',
Inspect = 'inspect',
}
+
+export const RUN_DETAILS_REFRESH_LIST_EVENT = 'RUN_DETAILS_REFRESH_LIST_EVENT';
diff --git a/frontend/src/pages/Runs/Details/index.tsx b/frontend/src/pages/Runs/Details/index.tsx
index 5195b4fdc0..9a1912a0bf 100644
--- a/frontend/src/pages/Runs/Details/index.tsx
+++ b/frontend/src/pages/Runs/Details/index.tsx
@@ -1,4 +1,5 @@
import React, { useEffect } from 'react';
+import { useBus } from 'react-bus';
import { useTranslation } from 'react-i18next';
import { Outlet, /*useNavigate,*/ useParams } from 'react-router-dom';
import Button from '@cloudscape-design/components/button';
@@ -15,7 +16,7 @@ import {
isAvailableStoppingForRun,
// isAvailableDeletingForRun,
} from '../utils';
-import { CodeTab } from './constants';
+import { CodeTab, RUN_DETAILS_REFRESH_LIST_EVENT } from './constants';
import styles from './styles.module.scss';
@@ -26,12 +27,12 @@ export const RunDetailsPage: React.FC = () => {
const paramProjectName = params.projectName ?? '';
const paramRunId = params.runId ?? '';
const [pushNotification] = useNotifications();
+ const bus = useBus();
const {
data: runData,
error: runError,
isLoading,
- refetch,
} = useGetRunQuery(
{
project_name: paramProjectName,
@@ -108,6 +109,10 @@ export const RunDetailsPage: React.FC = () => {
});
};
+ const refreshHandle = () => {
+ bus.emit(RUN_DETAILS_REFRESH_LIST_EVENT);
+ };
+
// const deleteClickHandle = () => {
// if (!runData) {
// return;
@@ -157,7 +162,7 @@ export const RunDetailsPage: React.FC = () => {
iconName="refresh"
disabled={isLoading}
ariaLabel={t('common.refresh')}
- onClick={refetch}
+ onClick={refreshHandle}
/>
>
}