mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
[PUI] Plugin panel context (#8190)
* Add server-side context for panel plugin rendering * Add "context" to PluginContext type * Pass server context through to client-side rendering * Bump API version
This commit is contained in:
parent
35362347a7
commit
194640f55a
@ -1,13 +1,16 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 259
|
||||
INVENTREE_API_VERSION = 260
|
||||
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v260 - 2024-09-26 : https://github.com/inventree/InvenTree/pull/8190
|
||||
- Adds facility for server-side context data to be passed to client-side plugins
|
||||
|
||||
v259 - 2024-09-20 : https://github.com/inventree/InvenTree/pull/8137
|
||||
- Implements new API endpoint for enabling custom UI features via plugins
|
||||
|
||||
|
@ -19,6 +19,7 @@ class CustomPanel(TypedDict):
|
||||
label: The label of the panel (required, human readable).
|
||||
icon: The icon of the panel (optional, must be a valid icon identifier).
|
||||
content: The content of the panel (optional, raw HTML).
|
||||
context: Optional context data (dict / JSON) which will be passed to the front-end rendering function
|
||||
source: The source of the panel (optional, path to a JavaScript file).
|
||||
"""
|
||||
|
||||
@ -26,6 +27,7 @@ class CustomPanel(TypedDict):
|
||||
label: str
|
||||
icon: str
|
||||
content: str
|
||||
context: dict
|
||||
source: str
|
||||
|
||||
|
||||
@ -87,6 +89,7 @@ class UserInterfaceMixin:
|
||||
'label': 'Panel Title', # The title of the panel (required, human readable)
|
||||
'icon': 'icon-name', # Icon name (optional, must be a valid icon identifier)
|
||||
'content': '<p>Panel content</p>', # HTML content to be rendered in the panel (optional)
|
||||
'context': {'key': 'value'}, # Context data to be passed to the front-end rendering function (optional)
|
||||
'source': 'static/plugin/panel.js', # Path to a JavaScript file to be loaded (optional)
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ class PluginPanelSerializer(serializers.Serializer):
|
||||
# Following fields are optional
|
||||
'icon',
|
||||
'content',
|
||||
'context',
|
||||
'source',
|
||||
]
|
||||
|
||||
@ -43,6 +44,10 @@ class PluginPanelSerializer(serializers.Serializer):
|
||||
label=_('Panel Content (HTML)'), required=False, allow_blank=True
|
||||
)
|
||||
|
||||
context = serializers.JSONField(
|
||||
label=_('Panel Context (JSON)'), required=False, allow_null=True, default=None
|
||||
)
|
||||
|
||||
source = serializers.CharField(
|
||||
label=_('Panel Source (javascript)'), required=False, allow_blank=True
|
||||
)
|
||||
|
@ -1,7 +1,11 @@
|
||||
"""Sample plugin which demonstrates user interface integrations."""
|
||||
|
||||
import random
|
||||
import time
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from InvenTree.version import INVENTREE_SW_VERSION
|
||||
from part.models import Part
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.helpers import render_template, render_text
|
||||
@ -15,7 +19,7 @@ class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlug
|
||||
SLUG = 'sampleui'
|
||||
TITLE = 'Sample User Interface Plugin'
|
||||
DESCRIPTION = 'A sample plugin which demonstrates user interface integrations'
|
||||
VERSION = '1.0'
|
||||
VERSION = '1.1'
|
||||
|
||||
SETTINGS = {
|
||||
'ENABLE_PART_PANELS': {
|
||||
@ -81,11 +85,18 @@ class SampleUserInterfacePlugin(SettingsMixin, UserInterfaceMixin, InvenTreePlug
|
||||
})
|
||||
|
||||
# A dynamic panel which will be injected into the UI (loaded from external file)
|
||||
# Note that we additionally provide some "context" data to the front-end render function
|
||||
if self.get_setting('ENABLE_DYNAMIC_PANEL'):
|
||||
panels.append({
|
||||
'name': 'dynamic_panel',
|
||||
'label': 'Dynamic Part Panel',
|
||||
'source': '/static/plugin/sample_panel.js',
|
||||
'context': {
|
||||
'version': INVENTREE_SW_VERSION,
|
||||
'plugin_version': self.VERSION,
|
||||
'random': random.randint(1, 100),
|
||||
'time': time.time(),
|
||||
},
|
||||
'icon': 'part',
|
||||
})
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
* as well as dynamically hidden, based on the provided context.
|
||||
*/
|
||||
|
||||
export function renderPanel(target, context) {
|
||||
export function renderPanel(target, data) {
|
||||
|
||||
if (!target) {
|
||||
console.error("No target provided to renderPanel");
|
||||
@ -22,13 +22,22 @@ export function renderPanel(target, context) {
|
||||
<p>It can be hidden or displayed based on the provided context.</p>
|
||||
|
||||
<hr>
|
||||
<h5>Context:</h5>
|
||||
<h5>Client Context:</h5>
|
||||
|
||||
<ul>
|
||||
<li>Username: ${context.user.username()}</li>
|
||||
<li>Is Staff: ${context.user.isStaff() ? "YES": "NO"}</li>
|
||||
<li>Model Type: ${context.model}</li>
|
||||
<li>Instance ID: ${context.id}</li>
|
||||
<li>Username: ${data.user.username()}</li>
|
||||
<li>Is Staff: ${data.user.isStaff() ? "YES": "NO"}</li>
|
||||
<li>Model Type: ${data.model}</li>
|
||||
<li>Instance ID: ${data.id}</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h5>Server Context:</h5>
|
||||
<ul>
|
||||
<li>Server Version: ${data.context.version}</li>
|
||||
<li>Plugin Version: ${data.context.plugin_version}</li>
|
||||
<li>Random Number: ${data.context.random}</li>
|
||||
<li>Time: ${data.context.time}</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ export type PluginPanelProps = {
|
||||
label: string;
|
||||
icon?: string;
|
||||
content?: string;
|
||||
context?: any;
|
||||
source?: string;
|
||||
};
|
||||
|
||||
@ -82,7 +83,9 @@ export default function PluginPanelContent({
|
||||
func(ref.current, pluginContext);
|
||||
setError('');
|
||||
} catch (error) {
|
||||
setError(t`Error occurred while rendering plugin content`);
|
||||
setError(
|
||||
t`Error occurred while rendering plugin content: ${error}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
setError(t`Plugin did not provide panel rendering function`);
|
||||
|
@ -111,6 +111,11 @@ export function usePluginPanels({
|
||||
);
|
||||
const isHidden: boolean = panelState[identifier] ?? true;
|
||||
|
||||
const pluginContext: any = {
|
||||
...contextData,
|
||||
context: props.context
|
||||
};
|
||||
|
||||
return {
|
||||
name: identifier,
|
||||
label: props.label,
|
||||
@ -118,7 +123,7 @@ export function usePluginPanels({
|
||||
content: (
|
||||
<PluginPanelContent
|
||||
pluginProps={props}
|
||||
pluginContext={contextData}
|
||||
pluginContext={pluginContext}
|
||||
/>
|
||||
),
|
||||
hidden: isHidden
|
||||
|
Loading…
x
Reference in New Issue
Block a user