2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-03-04 03:11:46 +00:00

Expose monitorDataOutput hook to plugin library (#11453)

- Allow plugins to process data outputs
This commit is contained in:
Oliver
2026-03-03 15:10:03 +11:00
committed by GitHub
parent 41a617febd
commit 948818bc78
5 changed files with 138 additions and 102 deletions

View File

@@ -2,6 +2,10 @@
This file contains historical changelog information for the InvenTree UI components library.
### 0.8.0 - March 2026
Exposes the `monitorDataOutput` hook, which allows plugins to monitor the output of a long-running task and display notifications when the task is complete. This is useful for plugins that need to perform long-running tasks and want to provide feedback to the user when the task is complete.
### 0.7.0 - October 2025
Exposes stock adjustment forms to plugins, allowing plugins to adjust stock adjustments using the common InvenTree UI form components.

View File

@@ -0,0 +1,122 @@
import { t } from '@lingui/core/macro';
import { useDocumentVisibility } from '@mantine/hooks';
import { notifications, showNotification } from '@mantine/notifications';
import { IconCircleCheck, IconExclamationCircle } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import type { AxiosInstance } from 'axios';
import { useEffect, useState } from 'react';
import { ProgressBar } from '../components/ProgressBar';
import { ApiEndpoints } from '../enums/ApiEndpoints';
import { apiUrl } from '../functions/Api';
/**
* Hook for monitoring a data output process running on the server
*/
export default function monitorDataOutput({
api,
title,
hostname,
id
}: {
api: AxiosInstance;
title: string;
hostname?: string;
id?: number;
}) {
const visibility = useDocumentVisibility();
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
if (!!id) {
setLoading(true);
showNotification({
id: `data-output-${id}`,
title: title,
loading: true,
autoClose: false,
withCloseButton: false,
message: <ProgressBar size='lg' value={0} progressLabel />
});
} else setLoading(false);
}, [id, title]);
useQuery({
enabled: !!id && loading && visibility === 'visible',
refetchInterval: 500,
queryKey: ['data-output', id, title],
queryFn: () =>
api
.get(apiUrl(ApiEndpoints.data_output, id))
.then((response) => {
const data = response?.data ?? {};
if (!!data.errors || !!data.error) {
setLoading(false);
const error: string =
data?.error ?? data?.errors?.error ?? t`Process failed`;
notifications.update({
id: `data-output-${id}`,
loading: false,
icon: <IconExclamationCircle />,
autoClose: 2500,
title: title,
message: error,
color: 'red'
});
} else if (data.complete) {
setLoading(false);
notifications.update({
id: `data-output-${id}`,
loading: false,
autoClose: 2500,
title: title,
message: t`Process completed successfully`,
color: 'green',
icon: <IconCircleCheck />
});
if (data.output) {
const url = data.output;
const base = hostname ?? window.location.hostname;
const downloadUrl = new URL(url, base);
window.open(downloadUrl.toString(), '_blank');
}
} else {
notifications.update({
id: `data-output-${id}`,
loading: true,
autoClose: false,
withCloseButton: false,
message: (
<ProgressBar
size='lg'
maximum={data.total}
value={data.progress}
progressLabel={data.total > 0}
animated
/>
)
});
}
return data;
})
.catch(() => {
setLoading(false);
notifications.update({
id: `data-output-${id}`,
loading: false,
autoClose: 2500,
title: title,
message: t`Process failed`,
color: 'red'
});
return {};
})
});
}

View File

@@ -71,3 +71,6 @@ export {
RowCancelAction,
RowActions
} from './components/RowActions';
// Shared hooks
export { default as monitorDataOutput } from './hooks/MonitorDataOutput';

View File

@@ -1,7 +1,7 @@
{
"name": "@inventreedb/ui",
"description": "UI components for the InvenTree project",
"version": "0.7.0",
"version": "0.8.0",
"private": false,
"type": "module",
"license": "MIT",

View File

@@ -1,14 +1,6 @@
import { ProgressBar } from '@lib/components/ProgressBar';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api';
import { t } from '@lingui/core/macro';
import { useDocumentVisibility } from '@mantine/hooks';
import { notifications, showNotification } from '@mantine/notifications';
import { IconCircleCheck, IconExclamationCircle } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import monitorDataOutput from '@lib/hooks/MonitorDataOutput';
import { useApi } from '../contexts/ApiContext';
import { generateUrl } from '../functions/urls';
import { useLocalState } from '../states/LocalState';
/**
* Hook for monitoring a data output process running on the server
@@ -21,97 +13,12 @@ export default function useDataOutput({
id?: number;
}) {
const api = useApi();
const { getHost } = useLocalState.getState();
const visibility = useDocumentVisibility();
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
if (!!id) {
setLoading(true);
showNotification({
id: `data-output-${id}`,
title: title,
loading: true,
autoClose: false,
withCloseButton: false,
message: <ProgressBar size='lg' value={0} progressLabel />
});
} else setLoading(false);
}, [id, title]);
useQuery({
enabled: !!id && loading && visibility === 'visible',
refetchInterval: 500,
queryKey: ['data-output', id, title],
queryFn: () =>
api
.get(apiUrl(ApiEndpoints.data_output, id))
.then((response) => {
const data = response?.data ?? {};
if (!!data.errors || !!data.error) {
setLoading(false);
const error: string =
data?.error ?? data?.errors?.error ?? t`Process failed`;
notifications.update({
id: `data-output-${id}`,
loading: false,
icon: <IconExclamationCircle />,
autoClose: 2500,
title: title,
message: error,
color: 'red'
});
} else if (data.complete) {
setLoading(false);
notifications.update({
id: `data-output-${id}`,
loading: false,
autoClose: 2500,
title: title,
message: t`Process completed successfully`,
color: 'green',
icon: <IconCircleCheck />
});
if (data.output) {
const url = generateUrl(data.output);
window.open(url.toString(), '_blank');
}
} else {
notifications.update({
id: `data-output-${id}`,
loading: true,
autoClose: false,
withCloseButton: false,
message: (
<ProgressBar
size='lg'
maximum={data.total}
value={data.progress}
progressLabel={data.total > 0}
animated
/>
)
});
}
return data;
})
.catch(() => {
setLoading(false);
notifications.update({
id: `data-output-${id}`,
loading: false,
autoClose: 2500,
title: title,
message: t`Process failed`,
color: 'red'
});
return {};
})
return monitorDataOutput({
api: api,
title: title,
id: id,
hostname: getHost()
});
}