2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-04-25 12:33:33 +00:00

refactor(frontend): make plugin support for panels more generic (#11787)

* feat(frontend): add more generic plugin support on PanelGroup

* refactor exsisting use cases

* add plugin panel support to StockLocation
closes https://github.com/inventree/InvenTree/issues/11782

* stop re-using model field

* re-order types

* add panel support for core

* change keys

see https://github.com/inventree/InvenTree/pull/11787#discussion_r3127410867
This commit is contained in:
Matthias Mair
2026-04-24 00:34:21 +02:00
committed by GitHub
parent 21d5edb56a
commit de3266cdf6
11 changed files with 60 additions and 23 deletions
+13
View File
@@ -38,3 +38,16 @@ export enum ModelType {
selectionentry = 'selectionentry', selectionentry = 'selectionentry',
error = 'error' error = 'error'
} }
export enum PluginPanelKey {
// settings / admin
admincenter = 'admincenter',
systemsettings = 'systemsettings',
usersettings = 'usersettings',
// generic
core = 'core',
// landing pages
purchasing = 'purchasing',
sales = 'sales',
manufacturing = 'manufacturing'
}
@@ -33,7 +33,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import { Boundary } from '@lib/components/Boundary'; import { Boundary } from '@lib/components/Boundary';
import type { ModelType } from '@lib/enums/ModelType'; import type { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { identifierString } from '@lib/functions/Conversion'; import { identifierString } from '@lib/functions/Conversion';
import { cancelEvent } from '@lib/functions/Events'; import { cancelEvent } from '@lib/functions/Events';
import { eventModified, getBaseUrl } from '@lib/functions/Navigation'; import { eventModified, getBaseUrl } from '@lib/functions/Navigation';
@@ -61,6 +61,8 @@ import * as classes from './PanelGroup.css';
* @param selectedPanel - The currently selected panel * @param selectedPanel - The currently selected panel
* @param onPanelChange - Callback when the active panel changes * @param onPanelChange - Callback when the active panel changes
* @param collapsible - If true, the panel group can be collapsed (defaults to true) * @param collapsible - If true, the panel group can be collapsed (defaults to true)
* @param pluginPanelWithoutId - If true, the panel group will load plugin panels even with no id provided
* @param pluginPanelKey - The plugin panel key to use when loading plugin panels for this group from the backend
*/ */
export type PanelProps = { export type PanelProps = {
pageKey: string; pageKey: string;
@@ -68,11 +70,13 @@ export type PanelProps = {
groups?: PanelGroupType[]; groups?: PanelGroupType[];
instance?: any; instance?: any;
reloadInstance?: () => void; reloadInstance?: () => void;
model?: ModelType | string; model?: ModelType;
id?: number | null; id?: number | null;
selectedPanel?: string; selectedPanel?: string;
onPanelChange?: (panel: string) => void; onPanelChange?: (panel: string) => void;
collapsible?: boolean; collapsible?: boolean;
pluginPanelWithoutId?: boolean;
pluginPanelKey?: PluginPanelKey;
}; };
function BasePanelGroup({ function BasePanelGroup({
@@ -85,7 +89,9 @@ function BasePanelGroup({
instance, instance,
model, model,
id, id,
collapsible = true collapsible = true,
pluginPanelWithoutId = false,
pluginPanelKey
}: Readonly<PanelProps>): ReactNode { }: Readonly<PanelProps>): ReactNode {
const localState = useLocalState(); const localState = useLocalState();
const location = useLocation(); const location = useLocation();
@@ -96,9 +102,17 @@ function BasePanelGroup({
const [expanded, setExpanded] = useState<boolean>(true); const [expanded, setExpanded] = useState<boolean>(true);
// Hook to load plugins for this panel // Hook to load plugins for this panel
const _pluginId = useMemo(() => {
if (id === undefined && pluginPanelWithoutId) return null;
return id;
}, [id, pluginPanelWithoutId]);
const _pluginKey = useMemo(() => {
if (model === undefined && pluginPanelWithoutId) return pluginPanelKey;
return model;
}, [model, pluginPanelWithoutId, pluginPanelKey]);
const pluginPanelSet = usePluginPanels({ const pluginPanelSet = usePluginPanels({
id: id, id: _pluginId,
model: model, model: _pluginKey,
instance: instance, instance: instance,
reloadFunc: reloadInstance reloadFunc: reloadInstance
}); });
+2 -2
View File
@@ -2,7 +2,7 @@ import { type UseQueryResult, useQuery } from '@tanstack/react-query';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import type { ModelType } from '@lib/enums/ModelType'; import type { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { apiUrl } from '@lib/functions/Api'; import { apiUrl } from '@lib/functions/Api';
import type { InvenTreePluginContext } from '@lib/types/Plugins'; import type { InvenTreePluginContext } from '@lib/types/Plugins';
import { api } from '../App'; import { api } from '../App';
@@ -39,7 +39,7 @@ export function usePluginPanels({
}: { }: {
instance?: any; instance?: any;
reloadFunc?: () => void; reloadFunc?: () => void;
model?: ModelType | string; model?: ModelType | PluginPanelKey;
id?: string | number | null; id?: string | number | null;
}): PluginPanelSet { }): PluginPanelSet {
const globalSettings = useGlobalSettingsState(); const globalSettings = useGlobalSettingsState();
@@ -22,6 +22,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { lazy, useMemo } from 'react'; import { lazy, useMemo } from 'react';
import { PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles'; import { UserRoles } from '@lib/enums/Roles';
import PermissionDenied from '../../../../components/errors/PermissionDenied'; import PermissionDenied from '../../../../components/errors/PermissionDenied';
import PageTitle from '../../../../components/nav/PageTitle'; import PageTitle from '../../../../components/nav/PageTitle';
@@ -303,8 +304,8 @@ export default function AdminCenter() {
panels={adminCenterPanels} panels={adminCenterPanels}
groups={grouping} groups={grouping}
collapsible={true} collapsible={true}
model='admincenter' pluginPanelWithoutId
id={null} pluginPanelKey={PluginPanelKey.admincenter}
/> />
</Stack> </Stack>
) : ( ) : (
@@ -20,6 +20,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { lazy, useMemo } from 'react'; import { lazy, useMemo } from 'react';
import { PluginPanelKey } from '@lib/enums/ModelType';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import PermissionDenied from '../../../components/errors/PermissionDenied'; import PermissionDenied from '../../../components/errors/PermissionDenied';
import PageTitle from '../../../components/nav/PageTitle'; import PageTitle from '../../../components/nav/PageTitle';
@@ -376,8 +377,8 @@ export default function SystemSettings() {
<PanelGroup <PanelGroup
pageKey='system-settings' pageKey='system-settings'
panels={systemSettingsPanels} panels={systemSettingsPanels}
model='systemsettings' pluginPanelWithoutId
id={null} pluginPanelKey={PluginPanelKey.systemsettings}
/> />
</Stack> </Stack>
) : ( ) : (
@@ -11,6 +11,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { lazy, useMemo } from 'react'; import { lazy, useMemo } from 'react';
import { PluginPanelKey } from '@lib/enums/ModelType';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import PageTitle from '../../../components/nav/PageTitle'; import PageTitle from '../../../components/nav/PageTitle';
import { SettingsHeader } from '../../../components/nav/SettingsHeader'; import { SettingsHeader } from '../../../components/nav/SettingsHeader';
@@ -154,8 +155,8 @@ export default function UserSettings() {
<PanelGroup <PanelGroup
pageKey='user-settings' pageKey='user-settings'
panels={userSettingsPanels} panels={userSettingsPanels}
model='usersettings' pluginPanelWithoutId
id={null} pluginPanelKey={PluginPanelKey.usersettings}
/> />
</Stack> </Stack>
</> </>
+3 -3
View File
@@ -8,7 +8,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { ModelType } from '@lib/enums/ModelType'; import { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles'; import { UserRoles } from '@lib/enums/Roles';
import type { TableFilter } from '@lib/types/Filters'; import type { TableFilter } from '@lib/types/Filters';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
@@ -102,8 +102,8 @@ export default function BuildIndex() {
<PanelGroup <PanelGroup
pageKey='build-index' pageKey='build-index'
panels={panels} panels={panels}
model='manufacturing' pluginPanelWithoutId
id={null} pluginPanelKey={PluginPanelKey.manufacturing}
/> />
</Stack> </Stack>
); );
+7 -1
View File
@@ -3,6 +3,7 @@ import { Stack } from '@mantine/core';
import { IconUser, IconUsersGroup } from '@tabler/icons-react'; import { IconUser, IconUsersGroup } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { PluginPanelKey } from '@lib/enums/ModelType';
import PermissionDenied from '../../components/errors/PermissionDenied'; import PermissionDenied from '../../components/errors/PermissionDenied';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup } from '../../components/panels/PanelGroup'; import { PanelGroup } from '../../components/panels/PanelGroup';
@@ -44,7 +45,12 @@ export default function CoreIndex() {
return ( return (
<Stack> <Stack>
<PageDetail title={t`System Overview`} /> <PageDetail title={t`System Overview`} />
<PanelGroup pageKey='core-index' panels={panels} id={null} /> <PanelGroup
pageKey='core-index'
panels={panels}
pluginPanelWithoutId
pluginPanelKey={PluginPanelKey.core}
/>
</Stack> </Stack>
); );
} }
@@ -12,7 +12,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { ModelType } from '@lib/enums/ModelType'; import { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles'; import { UserRoles } from '@lib/enums/Roles';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import OrderCalendar from '../../components/calendar/OrderCalendar'; import OrderCalendar from '../../components/calendar/OrderCalendar';
@@ -215,8 +215,8 @@ export default function PurchasingIndex() {
<PanelGroup <PanelGroup
pageKey='purchasing-index' pageKey='purchasing-index'
panels={panels} panels={panels}
model={'purchasing'} pluginPanelWithoutId
id={null} pluginPanelKey={PluginPanelKey.purchasing}
/> />
</Stack> </Stack>
); );
+3 -3
View File
@@ -11,7 +11,7 @@ import {
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { ModelType } from '@lib/enums/ModelType'; import { ModelType, PluginPanelKey } from '@lib/enums/ModelType';
import { UserRoles } from '@lib/enums/Roles'; import { UserRoles } from '@lib/enums/Roles';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import OrderCalendar from '../../components/calendar/OrderCalendar'; import OrderCalendar from '../../components/calendar/OrderCalendar';
@@ -170,8 +170,8 @@ export default function SalesIndex() {
<PanelGroup <PanelGroup
pageKey='sales-index' pageKey='sales-index'
panels={panels} panels={panels}
model={'sales'} pluginPanelWithoutId
id={null} pluginPanelKey={PluginPanelKey.sales}
/> />
</Stack> </Stack>
); );
@@ -481,6 +481,7 @@ export default function Stock() {
reloadInstance={refreshInstance} reloadInstance={refreshInstance}
id={location?.pk} id={location?.pk}
instance={location} instance={location}
pluginPanelWithoutId
/> />
</Stack> </Stack>
{stockAdjustActions.modals.map((modal) => modal.modal)} {stockAdjustActions.modals.map((modal) => modal.modal)}