2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-03-04 11:21:40 +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. 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 ### 0.7.0 - October 2025
Exposes stock adjustment forms to plugins, allowing plugins to adjust stock adjustments using the common InvenTree UI form components. 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, RowCancelAction,
RowActions RowActions
} from './components/RowActions'; } from './components/RowActions';
// Shared hooks
export { default as monitorDataOutput } from './hooks/MonitorDataOutput';

View File

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

View File

@@ -1,14 +1,6 @@
import { ProgressBar } from '@lib/components/ProgressBar'; import monitorDataOutput from '@lib/hooks/MonitorDataOutput';
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 { useApi } from '../contexts/ApiContext'; 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 * Hook for monitoring a data output process running on the server
@@ -21,97 +13,12 @@ export default function useDataOutput({
id?: number; id?: number;
}) { }) {
const api = useApi(); const api = useApi();
const { getHost } = useLocalState.getState();
const visibility = useDocumentVisibility(); return monitorDataOutput({
api: api,
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
if (!!id) {
setLoading(true);
showNotification({
id: `data-output-${id}`,
title: title, title: title,
loading: true, id: id,
autoClose: false, hostname: getHost()
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 {};
})
}); });
} }