Add some dummy data for the plugin panels

This commit is contained in:
Oliver Walters 2024-06-18 13:12:40 +00:00
parent 15ebbb6f04
commit 299839312f
6 changed files with 97 additions and 45 deletions

View File

@ -424,8 +424,33 @@ class PluginPanelList(APIView):
# Extract all plugins from the registry which provide custom panels # Extract all plugins from the registry which provide custom panels
for _plugin in registry.with_mixin('panel', active=True): for _plugin in registry.with_mixin('panel', active=True):
# TODO: Allow plugins to fill this data out
... ...
panels = [
{
'plugin': 'myplugin',
'name': 'test-plugin',
'label': 'My Plugin',
'icon': 'part',
'content': '<div>hello world</div>',
},
{
'plugin': 'myplugin',
'name': 'test-plugin-2',
'label': 'My Plugin 2',
'icon': 'email',
'content': '<div>hello world 2</div>',
},
{
'plugin': 'myplugin',
'name': 'test-plugin-3',
'label': 'My Plugin 3',
'icon': 'website',
'content': '<div>hello world 3</div>',
},
]
return Response(PluginSerializers.PluginPanelSerializer(panels, many=True).data) return Response(PluginSerializers.PluginPanelSerializer(panels, many=True).data)
@ -437,7 +462,7 @@ plugin_api_urls = [
'plugins/', 'plugins/',
include([ include([
path( path(
'panel/', 'panels/',
include([ include([
path('', PluginPanelList.as_view(), name='api-plugin-panel-list') path('', PluginPanelList.as_view(), name='api-plugin-panel-list')
]), ]),

View File

@ -311,9 +311,10 @@ class PluginPanelSerializer(serializers.Serializer):
class Meta: class Meta:
"""Meta for serializer.""" """Meta for serializer."""
fields = ['plugin', 'title', 'description', 'icon'] fields = ['plugin', 'name', 'label', 'icon']
plugin = serializers.CharField(label=_('Plugin Key')) plugin = serializers.CharField(label=_('Plugin Key'))
title = serializers.CharField(label=_('Panel Title')) name = serializers.CharField(label=_('Panel Name'))
description = serializers.CharField(label=_('Panel Description')) label = serializers.CharField(label=_('Panel Label'))
icon = serializers.CharField(label=_('Panel Icon')) icon = serializers.CharField(label=_('Panel Icon'))
content = serializers.CharField(label=_('Panel Content'))

View File

@ -144,6 +144,7 @@ export enum ApiEndpoints {
plugin_reload = 'plugins/reload/', plugin_reload = 'plugins/reload/',
plugin_activate = 'plugins/:key/activate/', plugin_activate = 'plugins/:key/activate/',
plugin_uninstall = 'plugins/:key/uninstall/', plugin_uninstall = 'plugins/:key/uninstall/',
plugin_panel_list = 'plugins/panels/',
// Machine API endpoints // Machine API endpoints
machine_types_list = 'machine/types/', machine_types_list = 'machine/types/',

View File

@ -50,6 +50,7 @@ import {
IconPaperclip, IconPaperclip,
IconPhone, IconPhone,
IconPhoto, IconPhoto,
IconPlug,
IconPoint, IconPoint,
IconPrinter, IconPrinter,
IconProgressCheck, IconProgressCheck,
@ -199,7 +200,8 @@ const icons = {
destination: IconFlag, destination: IconFlag,
repeat_destination: IconFlagShare, repeat_destination: IconFlagShare,
unlink: IconUnlink, unlink: IconUnlink,
success: IconCircleCheck success: IconCircleCheck,
plugin: IconPlug
}; };
export type InvenTreeIconType = keyof typeof icons; export type InvenTreeIconType = keyof typeof icons;

View File

@ -1,58 +1,70 @@
import { t } from '@lingui/macro';
import { Alert, Text } from '@mantine/core';
import { useTimeout } from '@mantine/hooks'; import { useTimeout } from '@mantine/hooks';
import { Icon24Hours } from '@tabler/icons-react'; import { Icon24Hours } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import { ReactNode, useEffect, useMemo, useState } from 'react'; import { ReactNode, useEffect, useMemo, useState } from 'react';
import { api } from '../App';
import { PanelType } from '../components/nav/Panel'; import { PanelType } from '../components/nav/Panel';
import { ApiEndpoints } from '../enums/ApiEndpoints';
import { identifierString } from '../functions/conversion';
import { InvenTreeIcon } from '../functions/icons';
import { apiUrl } from '../states/ApiState';
export interface PluginPanelState extends PanelType { export type PluginPanelState = {
pluginKey: string; panels: PanelType[];
targetType: string; };
targetId?: string | number | null;
// Placeholder content for a panel with no content
function PanelNoContent() {
return (
<Alert color="red" title={t`No Content`}>
<Text>{t`No content provided for this plugin`}</Text>
</Alert>
);
} }
export function usePluginPanel({ export function usePluginPanels({
pluginKey,
panelName,
targetModel, targetModel,
targetId targetId
}: { }: {
pluginKey: string;
panelName: string;
targetModel: string; targetModel: string;
targetId?: string | number | null; targetId?: string | number | null;
}): PluginPanelState { }): PluginPanelState {
// TODO: Query to fetch the "content" for the plugin const { isFetching, data } = useQuery({
queryKey: [targetModel, targetId],
queryFn: () => {
return api
.get(apiUrl(ApiEndpoints.plugin_panel_list), {
params: {
target_model: targetModel,
target_id: targetId
}
})
.then((response: any) => response.data)
.catch((error: any) => {
console.error('Failed to fetch plugin panels:', error);
return [];
});
}
});
const [loaded, setLoaded] = useState<boolean>(false); const panels: PanelType[] = useMemo(() => {
return (
const { start } = useTimeout(() => setLoaded(true), 5000); data?.map((panel: any) => {
const pluginKey = panel.plugin || 'plugin';
useEffect(() => { return {
start(); name: identifierString(`${pluginKey}-${panel.name}`),
console.log('starting timer!'); label: panel.label || t`Plugin Panel`,
}, []); icon: <InvenTreeIcon icon={panel.icon ?? 'plugin'} />,
content: panel.content || <PanelNoContent />
const content = useMemo(() => { };
return loaded ? ( }) ?? []
'plugin content loaded!'
) : (
<div>
<p>Plugin content goes here...</p>
<p>Plugin Key: {pluginKey}</p>
<p>Panel Name: {panelName}</p>
<p>Target Model: {targetModel}</p>
<p>Target ID: {targetId}</p>
</div>
); );
}, [loaded, pluginKey, panelName, targetModel, targetId]); }, [data]);
return { return {
content: content, panels: panels
name: panelName,
pluginKey: pluginKey,
targetType: targetModel,
targetId: targetId,
label: 'A plugin panel',
icon: <Icon24Hours />
}; };
} }

View File

@ -56,7 +56,8 @@ import {
import { PlaceholderPanel } from '../../components/items/Placeholder'; import { PlaceholderPanel } from '../../components/items/Placeholder';
import NavigationTree from '../../components/nav/NavigationTree'; import NavigationTree from '../../components/nav/NavigationTree';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; import { PanelType } from '../../components/nav/Panel';
import { PanelGroup } from '../../components/nav/PanelGroup';
import { formatPriceRange } from '../../defaults/formatters'; import { formatPriceRange } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType'; import { ModelType } from '../../enums/ModelType';
@ -75,6 +76,7 @@ import {
useEditApiFormModal useEditApiFormModal
} from '../../hooks/UseForm'; } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import { usePluginPanels } from '../../hooks/UsePluginPanels';
import { apiUrl } from '../../states/ApiState'; import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { BomTable } from '../../tables/bom/BomTable'; import { BomTable } from '../../tables/bom/BomTable';
@ -640,6 +642,15 @@ export default function PartDetail() {
]; ];
}, [id, part, user]); }, [id, part, user]);
const pluginPanels = usePluginPanels({
targetModel: ModelType.part,
targetId: id
});
const panels: PanelType[] = useMemo(() => {
return [...partPanels, ...pluginPanels.panels];
}, [partPanels, pluginPanels]);
const breadcrumbs = useMemo( const breadcrumbs = useMemo(
() => [ () => [
{ name: t`Parts`, url: '/part' }, { name: t`Parts`, url: '/part' },
@ -844,7 +855,7 @@ export default function PartDetail() {
}} }}
actions={partActions} actions={partActions}
/> />
<PanelGroup pageKey="part" panels={partPanels} /> <PanelGroup pageKey="part" panels={panels} />
{transferStockItems.modal} {transferStockItems.modal}
{countStockItems.modal} {countStockItems.modal}
</Stack> </Stack>