2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-09 03:03:41 +00:00

Refactor ImporterDrawer

- Use a single, globally accessible object
- Provide global state management
This commit is contained in:
Oliver Walters
2026-04-07 10:59:55 +00:00
parent 448d775690
commit 731e25a902
8 changed files with 139 additions and 111 deletions
@@ -0,0 +1,20 @@
import { useImporterState } from '../../states/ImporterState';
import ImporterDrawer from './ImporterDrawer';
export default function GlobalImporterDrawer() {
const isOpen = useImporterState((state) => state.isOpen);
const sessionId = useImporterState((state) => state.sessionId);
const closeImporter = useImporterState((state) => state.closeImporter);
if (!isOpen || sessionId === null) {
return null;
}
return (
<ImporterDrawer
sessionId={sessionId}
opened={isOpen}
onClose={closeImporter}
/>
);
}
@@ -21,6 +21,7 @@ import {
} from '../../states/SettingsStates'; } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { Boundary } from '../Boundary'; import { Boundary } from '../Boundary';
import GlobalImporterDrawer from '../importer/GlobalImporterDrawer';
import { ApiIcon } from '../items/ApiIcon'; import { ApiIcon } from '../items/ApiIcon';
import { useInvenTreeContext } from '../plugins/PluginContext'; import { useInvenTreeContext } from '../plugins/PluginContext';
import { callExternalPluginFunction } from '../plugins/PluginSource'; import { callExternalPluginFunction } from '../plugins/PluginSource';
@@ -118,6 +119,7 @@ export default function LayoutComponent() {
return ( return (
<ProtectedRoute> <ProtectedRoute>
<>
<Flex direction='column' mih='100vh'> <Flex direction='column' mih='100vh'>
<Header /> <Header />
<Container className={classes.layoutContent} size='100%'> <Container className={classes.layoutContent} size='100%'>
@@ -143,6 +145,8 @@ export default function LayoutComponent() {
/> />
)} )}
</Flex> </Flex>
<GlobalImporterDrawer />
</>
</ProtectedRoute> </ProtectedRoute>
); );
} }
+62
View File
@@ -0,0 +1,62 @@
/**
* This file contains a global state manager for the importer drawer, allowing it to be opened and closed from anywhere in the app without needing to pass props down through the component tree.
* The state includes the current session ID for the importer, as well as an optional callback function that can be executed when the importer is closed.
* This allows for flexible handling of importer actions, such as refreshing data after an import is completed.
* The state is managed using the Zustand library, which provides a simple and efficient way to create global state in React applications.
* The `useImporterState` hook can be used to access and manipulate the importer state from any component, while the `openGlobalImporter` and `closeGlobalImporter` functions provide convenient ways to control the importer from outside of React components.
*/
import { create } from 'zustand';
export interface ImporterOpenOptions {
onClose?: () => void;
}
interface ImporterStateProps {
isOpen: boolean;
sessionId: number | null;
onCloseCallback?: () => void;
openImporter: (sessionId: number, options?: ImporterOpenOptions) => void;
closeImporter: () => void;
}
export const useImporterState = create<ImporterStateProps>()((set, get) => ({
isOpen: false,
sessionId: null,
onCloseCallback: undefined,
openImporter: (sessionId: number, options?: ImporterOpenOptions) => {
set({
sessionId,
isOpen: true,
onCloseCallback: options?.onClose
});
},
closeImporter: () => {
const callback = get().onCloseCallback;
set({
sessionId: null,
isOpen: false,
onCloseCallback: undefined
});
callback?.();
}
}));
export function openGlobalImporter(
sessionId: number,
options?: ImporterOpenOptions
) {
useImporterState.getState().openImporter(sessionId, options);
}
export function closeGlobalImporter() {
useImporterState.getState().closeImporter();
}
export function getGlobalImporterState() {
return useImporterState.getState();
}
+5 -18
View File
@@ -26,7 +26,6 @@ import {
import { type ReactNode, useCallback, useMemo, useState } from 'react'; import { type ReactNode, useCallback, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Thumbnail } from '../../components/images/Thumbnail'; import { Thumbnail } from '../../components/images/Thumbnail';
import ImporterDrawer from '../../components/importer/ImporterDrawer';
import { ActionDropdown } from '../../components/items/ActionDropdown'; import { ActionDropdown } from '../../components/items/ActionDropdown';
import { RenderPart } from '../../components/render/Part'; import { RenderPart } from '../../components/render/Part';
import { useApi } from '../../contexts/ApiContext'; import { useApi } from '../../contexts/ApiContext';
@@ -39,6 +38,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useImporterState } from '../../states/ImporterState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { import {
BooleanColumn, BooleanColumn,
@@ -81,12 +81,7 @@ export function BomTable({
const user = useUserState(); const user = useUserState();
const table = useTable('bom'); const table = useTable('bom');
const navigate = useNavigate(); const navigate = useNavigate();
const openImporter = useImporterState((state) => state.openImporter);
const [importOpened, setImportOpened] = useState<boolean>(false);
const [selectedSession, setSelectedSession] = useState<number | undefined>(
undefined
);
const tableColumns: TableColumn[] = useMemo(() => { const tableColumns: TableColumn[] = useMemo(() => {
return [ return [
@@ -501,8 +496,9 @@ export function BomTable({
title: t`Import BOM Data`, title: t`Import BOM Data`,
fields: importSessionFields, fields: importSessionFields,
onFormSuccess: (response: any) => { onFormSuccess: (response: any) => {
setSelectedSession(response.pk); openImporter(response.pk, {
setImportOpened(true); onClose: table.refreshTable
});
} }
}); });
@@ -690,15 +686,6 @@ export function BomTable({
}} }}
/> />
</Stack> </Stack>
<ImporterDrawer
sessionId={selectedSession ?? -1}
opened={selectedSession != undefined && importOpened}
onClose={() => {
setSelectedSession(undefined);
setImportOpened(false);
table.refreshTable();
}}
/>
</> </>
); );
} }
@@ -12,7 +12,6 @@ import type { TableColumn } from '@lib/types/Tables';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { IconFileUpload, IconPlus } from '@tabler/icons-react'; import { IconFileUpload, IconPlus } from '@tabler/icons-react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import ImporterDrawer from '../../components/importer/ImporterDrawer';
import { ActionDropdown } from '../../components/items/ActionDropdown'; import { ActionDropdown } from '../../components/items/ActionDropdown';
import { useParameterFields } from '../../forms/CommonForms'; import { useParameterFields } from '../../forms/CommonForms';
import { dataImporterSessionFields } from '../../forms/ImporterForms'; import { dataImporterSessionFields } from '../../forms/ImporterForms';
@@ -22,6 +21,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useImporterState } from '../../states/ImporterState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { import {
DateColumn, DateColumn,
@@ -129,12 +129,7 @@ export function ParameterTable({
const [selectedParameter, setSelectedParameter] = useState<any | undefined>( const [selectedParameter, setSelectedParameter] = useState<any | undefined>(
undefined undefined
); );
const openImporter = useImporterState((state) => state.openImporter);
const [importOpened, setImportOpened] = useState<boolean>(false);
const [selectedSession, setSelectedSession] = useState<number | undefined>(
undefined
);
const importSessionFields = useMemo(() => { const importSessionFields = useMemo(() => {
const fields = dataImporterSessionFields({ const fields = dataImporterSessionFields({
@@ -154,8 +149,9 @@ export function ParameterTable({
title: t`Import Parameters`, title: t`Import Parameters`,
fields: importSessionFields, fields: importSessionFields,
onFormSuccess: (response: any) => { onFormSuccess: (response: any) => {
setSelectedSession(response.pk); openImporter(response.pk, {
setImportOpened(true); onClose: table.refreshTable
});
} }
}); });
@@ -262,15 +258,6 @@ export function ParameterTable({
} }
}} }}
/> />
<ImporterDrawer
sessionId={selectedSession ?? -1}
opened={selectedSession !== undefined && importOpened}
onClose={() => {
setSelectedSession(undefined);
setImportOpened(false);
table.refreshTable();
}}
/>
</> </>
); );
} }
+5 -18
View File
@@ -20,7 +20,6 @@ import {
IconShoppingCart IconShoppingCart
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { type ReactNode, useCallback, useMemo, useState } from 'react'; import { type ReactNode, useCallback, useMemo, useState } from 'react';
import ImporterDrawer from '../../components/importer/ImporterDrawer';
import { ActionDropdown } from '../../components/items/ActionDropdown'; import { ActionDropdown } from '../../components/items/ActionDropdown';
import ImportPartWizard from '../../components/wizards/ImportPartWizard'; import ImportPartWizard from '../../components/wizards/ImportPartWizard';
import OrderPartsWizard from '../../components/wizards/OrderPartsWizard'; import OrderPartsWizard from '../../components/wizards/OrderPartsWizard';
@@ -35,6 +34,7 @@ import {
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { usePluginsWithMixin } from '../../hooks/UsePlugins'; import { usePluginsWithMixin } from '../../hooks/UsePlugins';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useImporterState } from '../../states/ImporterState';
import { useGlobalSettingsState } from '../../states/SettingsStates'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { import {
@@ -357,12 +357,7 @@ export function PartListTable({
}); });
const user = useUserState(); const user = useUserState();
const globalSettings = useGlobalSettingsState(); const globalSettings = useGlobalSettingsState();
const openImporter = useImporterState((state) => state.openImporter);
const [importOpened, setImportOpened] = useState<boolean>(false);
const [selectedSession, setSelectedSession] = useState<number | undefined>(
undefined
);
const importSessionFields = useMemo(() => { const importSessionFields = useMemo(() => {
const fields = dataImporterSessionFields({ const fields = dataImporterSessionFields({
@@ -383,8 +378,9 @@ export function PartListTable({
title: t`Import Parts`, title: t`Import Parts`,
fields: importSessionFields, fields: importSessionFields,
onFormSuccess: (response: any) => { onFormSuccess: (response: any) => {
setSelectedSession(response.pk); openImporter(response.pk, {
setImportOpened(true); onClose: table.refreshTable
});
} }
}); });
@@ -598,15 +594,6 @@ export function PartListTable({
} }
}} }}
/> />
<ImporterDrawer
sessionId={selectedSession ?? -1}
opened={selectedSession != undefined && importOpened}
onClose={() => {
setSelectedSession(undefined);
setImportOpened(false);
table.refreshTable();
}}
/>
</> </>
); );
} }
@@ -21,7 +21,6 @@ import { formatDecimal } from '@lib/functions/Formatting';
import type { TableFilter } from '@lib/types/Filters'; import type { TableFilter } from '@lib/types/Filters';
import type { TableColumn } from '@lib/types/Tables'; import type { TableColumn } from '@lib/types/Tables';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import ImporterDrawer from '../../components/importer/ImporterDrawer';
import { RenderInstance } from '../../components/render/Instance'; import { RenderInstance } from '../../components/render/Instance';
import { formatCurrency } from '../../defaults/formatters'; import { formatCurrency } from '../../defaults/formatters';
import { dataImporterSessionFields } from '../../forms/ImporterForms'; import { dataImporterSessionFields } from '../../forms/ImporterForms';
@@ -36,6 +35,7 @@ import {
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import useStatusCodes from '../../hooks/UseStatusCodes'; import useStatusCodes from '../../hooks/UseStatusCodes';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useImporterState } from '../../states/ImporterState';
import { useGlobalSettingsState } from '../../states/SettingsStates'; import { useGlobalSettingsState } from '../../states/SettingsStates';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { import {
@@ -77,12 +77,7 @@ export function PurchaseOrderLineItemTable({
const globalSettings = useGlobalSettingsState(); const globalSettings = useGlobalSettingsState();
const navigate = useNavigate(); const navigate = useNavigate();
const user = useUserState(); const user = useUserState();
const openImporter = useImporterState((state) => state.openImporter);
// Data import
const [importOpened, setImportOpened] = useState<boolean>(false);
const [selectedSession, setSelectedSession] = useState<number | undefined>(
undefined
);
const importSessionFields = useMemo(() => { const importSessionFields = useMemo(() => {
const fields = dataImporterSessionFields({ const fields = dataImporterSessionFields({
@@ -115,8 +110,9 @@ export function PurchaseOrderLineItemTable({
title: t`Import Line Items`, title: t`Import Line Items`,
fields: importSessionFields, fields: importSessionFields,
onFormSuccess: (response: any) => { onFormSuccess: (response: any) => {
setSelectedSession(response.pk); openImporter(response.pk, {
setImportOpened(true); onClose: table.refreshTable
});
} }
}); });
@@ -452,15 +448,6 @@ export function PurchaseOrderLineItemTable({
modelField: 'part' modelField: 'part'
}} }}
/> />
<ImporterDrawer
sessionId={selectedSession ?? -1}
opened={selectedSession != undefined && importOpened}
onClose={() => {
setSelectedSession(undefined);
setImportOpened(false);
table.refreshTable();
}}
/>
</> </>
); );
} }
@@ -9,7 +9,6 @@ import { ModelType } from '@lib/enums/ModelType';
import { apiUrl } from '@lib/functions/Api'; import { apiUrl } from '@lib/functions/Api';
import type { TableFilter } from '@lib/types/Filters'; import type { TableFilter } from '@lib/types/Filters';
import type { TableColumn } from '@lib/types/Tables'; import type { TableColumn } from '@lib/types/Tables';
import ImporterDrawer from '../../components/importer/ImporterDrawer';
import { AttachmentLink } from '../../components/items/AttachmentLink'; import { AttachmentLink } from '../../components/items/AttachmentLink';
import { RenderUser } from '../../components/render/User'; import { RenderUser } from '../../components/render/User';
import { dataImporterSessionFields } from '../../forms/ImporterForms'; import { dataImporterSessionFields } from '../../forms/ImporterForms';
@@ -19,14 +18,14 @@ import {
useDeleteApiFormModal useDeleteApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable'; import { useTable } from '../../hooks/UseTable';
import { useImporterState } from '../../states/ImporterState';
import { DateColumn, StatusColumn } from '../ColumnRenderers'; import { DateColumn, StatusColumn } from '../ColumnRenderers';
import { StatusFilterOptions, UserFilter } from '../Filter'; import { StatusFilterOptions, UserFilter } from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable'; import { InvenTreeTable } from '../InvenTreeTable';
export default function ImportSessionTable() { export default function ImportSessionTable() {
const table = useTable('importsession'); const table = useTable('importsession');
const openImporter = useImporterState((state) => state.openImporter);
const [opened, setOpened] = useState<boolean>(false);
const [selectedSession, setSelectedSession] = useState<number | undefined>( const [selectedSession, setSelectedSession] = useState<number | undefined>(
undefined undefined
@@ -47,7 +46,9 @@ export default function ImportSessionTable() {
}), }),
onFormSuccess: (response: any) => { onFormSuccess: (response: any) => {
setSelectedSession(response.pk); setSelectedSession(response.pk);
setOpened(true); openImporter(response.pk, {
onClose: table.refreshTable
});
table.refreshTable(); table.refreshTable();
} }
}); });
@@ -159,19 +160,12 @@ export default function ImportSessionTable() {
enableSelection: true, enableSelection: true,
onRowClick: (record: any) => { onRowClick: (record: any) => {
setSelectedSession(record.pk); setSelectedSession(record.pk);
setOpened(true); openImporter(record.pk, {
onClose: table.refreshTable
});
} }
}} }}
/> />
<ImporterDrawer
sessionId={selectedSession ?? -1}
opened={selectedSession !== undefined && opened}
onClose={() => {
setSelectedSession(undefined);
setOpened(false);
table.refreshTable();
}}
/>
</> </>
); );
} }