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:
@@ -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.
|
||||
|
||||
122
src/frontend/lib/hooks/MonitorDataOutput.tsx
Normal file
122
src/frontend/lib/hooks/MonitorDataOutput.tsx
Normal 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 {};
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -71,3 +71,6 @@ export {
|
||||
RowCancelAction,
|
||||
RowActions
|
||||
} from './components/RowActions';
|
||||
|
||||
// Shared hooks
|
||||
export { default as monitorDataOutput } from './hooks/MonitorDataOutput';
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user