@perses-dev/dashboards 0.0.0-snapshot-time-zone-selector-946f408 → 0.0.0-snapshot-panel-actions-520389b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/DashboardToolbar/DashboardToolbar.js +98 -109
- package/dist/cjs/components/Panel/Panel.js +60 -1
- package/dist/cjs/components/Panel/PanelActions.js +86 -16
- package/dist/cjs/components/Panel/PanelHeader.js +47 -3
- package/dist/cjs/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.js +1 -13
- package/dist/cjs/components/SaveDashboardButton/SaveDashboardButton.js +3 -10
- package/dist/cjs/context/DashboardProvider/DashboardProvider.js +2 -4
- package/dist/cjs/context/useDashboard.js +1 -3
- package/dist/cjs/views/ViewDashboard/ViewDashboard.js +0 -2
- package/dist/components/DashboardToolbar/DashboardToolbar.d.ts.map +1 -1
- package/dist/components/DashboardToolbar/DashboardToolbar.js +100 -111
- package/dist/components/DashboardToolbar/DashboardToolbar.js.map +1 -1
- package/dist/components/Panel/Panel.d.ts +5 -0
- package/dist/components/Panel/Panel.d.ts.map +1 -1
- package/dist/components/Panel/Panel.js +60 -1
- package/dist/components/Panel/Panel.js.map +1 -1
- package/dist/components/Panel/PanelActions.d.ts +5 -3
- package/dist/components/Panel/PanelActions.d.ts.map +1 -1
- package/dist/components/Panel/PanelActions.js +87 -17
- package/dist/components/Panel/PanelActions.js.map +1 -1
- package/dist/components/Panel/PanelHeader.d.ts +5 -1
- package/dist/components/Panel/PanelHeader.d.ts.map +1 -1
- package/dist/components/Panel/PanelHeader.js +47 -3
- package/dist/components/Panel/PanelHeader.js.map +1 -1
- package/dist/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.d.ts.map +1 -1
- package/dist/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.js +2 -14
- package/dist/components/SaveChangesConfirmationDialog/SaveChangesConfirmationDialog.js.map +1 -1
- package/dist/components/SaveDashboardButton/SaveDashboardButton.d.ts.map +1 -1
- package/dist/components/SaveDashboardButton/SaveDashboardButton.js +4 -11
- package/dist/components/SaveDashboardButton/SaveDashboardButton.js.map +1 -1
- package/dist/context/DashboardProvider/DashboardProvider.d.ts +0 -1
- package/dist/context/DashboardProvider/DashboardProvider.d.ts.map +1 -1
- package/dist/context/DashboardProvider/DashboardProvider.js +3 -5
- package/dist/context/DashboardProvider/DashboardProvider.js.map +1 -1
- package/dist/context/DashboardProvider/save-changes-dialog-slice.d.ts +1 -2
- package/dist/context/DashboardProvider/save-changes-dialog-slice.d.ts.map +1 -1
- package/dist/context/DashboardProvider/save-changes-dialog-slice.js.map +1 -1
- package/dist/context/useDashboard.d.ts.map +1 -1
- package/dist/context/useDashboard.js +1 -3
- package/dist/context/useDashboard.js.map +1 -1
- package/dist/views/ViewDashboard/ViewDashboard.d.ts.map +1 -1
- package/dist/views/ViewDashboard/ViewDashboard.js +1 -3
- package/dist/views/ViewDashboard/ViewDashboard.js.map +1 -1
- package/package.json +5 -5
|
@@ -24,7 +24,6 @@ const _jsxruntime = require("react/jsx-runtime");
|
|
|
24
24
|
const _material = require("@mui/material");
|
|
25
25
|
const _components = require("@perses-dev/components");
|
|
26
26
|
const _pluginsystem = require("@perses-dev/plugin-system");
|
|
27
|
-
const _react = require("react");
|
|
28
27
|
const _context = require("../../context");
|
|
29
28
|
const _AddPanelButton = require("../AddPanelButton");
|
|
30
29
|
const _AddGroupButton = require("../AddGroupButton");
|
|
@@ -40,126 +39,116 @@ const DashboardToolbar = (props)=>{
|
|
|
40
39
|
const { isEditMode } = (0, _context.useEditMode)();
|
|
41
40
|
const isBiggerThanSm = (0, _material.useMediaQuery)((0, _material.useTheme)().breakpoints.up('sm'));
|
|
42
41
|
const isBiggerThanMd = (0, _material.useMediaQuery)((0, _material.useTheme)().breakpoints.up('md'));
|
|
43
|
-
const { timeZone, setTimeZone } = (0, _pluginsystem.useTimeZone)();
|
|
44
|
-
const timeZoneOptions = (0, _components.getTimeZoneOptions)();
|
|
45
42
|
const dashboardTitle = dashboardTitleComponent ? dashboardTitleComponent : /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Typography, {
|
|
46
43
|
variant: "h2",
|
|
47
44
|
children: dashboardName
|
|
48
45
|
});
|
|
49
46
|
const testId = 'dashboard-toolbar';
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
children: [
|
|
66
|
-
dashboardTitle,
|
|
67
|
-
isEditMode ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
68
|
-
direction: "row",
|
|
69
|
-
gap: 1,
|
|
70
|
-
ml: "auto",
|
|
71
|
-
children: [
|
|
72
|
-
isReadonly && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Alert, {
|
|
73
|
-
severity: "warning",
|
|
74
|
-
sx: {
|
|
75
|
-
backgroundColor: 'transparent',
|
|
76
|
-
padding: 0
|
|
77
|
-
},
|
|
78
|
-
children: "Dashboard managed via code only. Download JSON and commit changes to save."
|
|
79
|
-
}),
|
|
80
|
-
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
81
|
-
direction: "row",
|
|
82
|
-
spacing: 0.5,
|
|
83
|
-
ml: 1,
|
|
84
|
-
whiteSpace: "nowrap",
|
|
85
|
-
children: [
|
|
86
|
-
isVariableEnabled && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Variables.EditVariablesButton, {}),
|
|
87
|
-
isDatasourceEnabled && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Datasources.EditDatasourcesButton, {}),
|
|
88
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_AddPanelButton.AddPanelButton, {}),
|
|
89
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_AddGroupButton.AddGroupButton, {})
|
|
90
|
-
]
|
|
91
|
-
}),
|
|
92
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_SaveDashboardButton.SaveDashboardButton, {
|
|
93
|
-
onSave: onSave,
|
|
94
|
-
isDisabled: isReadonly
|
|
95
|
-
}),
|
|
96
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Button, {
|
|
97
|
-
variant: "outlined",
|
|
98
|
-
onClick: onCancelButtonClick,
|
|
99
|
-
children: "Cancel"
|
|
100
|
-
})
|
|
101
|
-
]
|
|
102
|
-
}) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_jsxruntime.Fragment, {
|
|
103
|
-
children: isBiggerThanSm && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Stack, {
|
|
47
|
+
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_jsxruntime.Fragment, {
|
|
48
|
+
children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
49
|
+
"data-testid": testId,
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Box, {
|
|
52
|
+
px: 2,
|
|
53
|
+
py: 1.5,
|
|
54
|
+
display: "flex",
|
|
55
|
+
sx: {
|
|
56
|
+
backgroundColor: (theme)=>theme.palette.primary.main + (isEditMode ? '30' : '0')
|
|
57
|
+
},
|
|
58
|
+
children: [
|
|
59
|
+
dashboardTitle,
|
|
60
|
+
isEditMode ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
104
61
|
direction: "row",
|
|
105
62
|
gap: 1,
|
|
106
63
|
ml: "auto",
|
|
107
|
-
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_EditButton.EditButton, {
|
|
108
|
-
onClick: onEditButtonClick
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
]
|
|
113
|
-
}),
|
|
114
|
-
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Box, {
|
|
115
|
-
sx: {
|
|
116
|
-
display: 'flex',
|
|
117
|
-
width: '100%',
|
|
118
|
-
alignItems: 'start',
|
|
119
|
-
padding: (theme)=>theme.spacing(1, 2, 0, 2),
|
|
120
|
-
flexDirection: isBiggerThanMd ? 'row' : 'column',
|
|
121
|
-
flexWrap: 'nowrap',
|
|
122
|
-
gap: 1
|
|
123
|
-
},
|
|
124
|
-
children: [
|
|
125
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Box, {
|
|
126
|
-
width: "100%",
|
|
127
|
-
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.ErrorBoundary, {
|
|
128
|
-
FallbackComponent: _components.ErrorAlert,
|
|
129
|
-
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_DashboardStickyToolbar.DashboardStickyToolbar, {
|
|
130
|
-
initialVariableIsSticky: initialVariableIsSticky,
|
|
131
|
-
sx: {
|
|
132
|
-
backgroundColor: ({ palette })=>palette.background.default
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
}),
|
|
137
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Stack, {
|
|
138
|
-
direction: "row",
|
|
139
|
-
ml: "auto",
|
|
140
|
-
flexWrap: "wrap",
|
|
141
|
-
justifyContent: "end",
|
|
142
|
-
children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
143
|
-
direction: "row",
|
|
144
|
-
spacing: 1,
|
|
145
|
-
mt: 1,
|
|
146
|
-
ml: 1,
|
|
147
64
|
children: [
|
|
148
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
65
|
+
isReadonly && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Alert, {
|
|
66
|
+
severity: "warning",
|
|
67
|
+
sx: {
|
|
68
|
+
backgroundColor: 'transparent',
|
|
69
|
+
padding: 0
|
|
70
|
+
},
|
|
71
|
+
children: "Dashboard managed via code only. Download JSON and commit changes to save."
|
|
153
72
|
}),
|
|
154
|
-
/*#__PURE__*/ (0, _jsxruntime.
|
|
155
|
-
|
|
156
|
-
|
|
73
|
+
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
74
|
+
direction: "row",
|
|
75
|
+
spacing: 0.5,
|
|
76
|
+
ml: 1,
|
|
77
|
+
whiteSpace: "nowrap",
|
|
78
|
+
children: [
|
|
79
|
+
isVariableEnabled && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Variables.EditVariablesButton, {}),
|
|
80
|
+
isDatasourceEnabled && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Datasources.EditDatasourcesButton, {}),
|
|
81
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_AddPanelButton.AddPanelButton, {}),
|
|
82
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_AddGroupButton.AddGroupButton, {})
|
|
83
|
+
]
|
|
84
|
+
}),
|
|
85
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_SaveDashboardButton.SaveDashboardButton, {
|
|
86
|
+
onSave: onSave,
|
|
87
|
+
isDisabled: isReadonly
|
|
88
|
+
}),
|
|
89
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Button, {
|
|
90
|
+
variant: "outlined",
|
|
91
|
+
onClick: onCancelButtonClick,
|
|
92
|
+
children: "Cancel"
|
|
157
93
|
})
|
|
158
94
|
]
|
|
95
|
+
}) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_jsxruntime.Fragment, {
|
|
96
|
+
children: isBiggerThanSm && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Stack, {
|
|
97
|
+
direction: "row",
|
|
98
|
+
gap: 1,
|
|
99
|
+
ml: "auto",
|
|
100
|
+
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_EditButton.EditButton, {
|
|
101
|
+
onClick: onEditButtonClick
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
}),
|
|
107
|
+
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Box, {
|
|
108
|
+
sx: {
|
|
109
|
+
display: 'flex',
|
|
110
|
+
width: '100%',
|
|
111
|
+
alignItems: 'start',
|
|
112
|
+
padding: (theme)=>theme.spacing(1, 2, 0, 2),
|
|
113
|
+
flexDirection: isBiggerThanMd ? 'row' : 'column',
|
|
114
|
+
flexWrap: 'nowrap',
|
|
115
|
+
gap: 1
|
|
116
|
+
},
|
|
117
|
+
children: [
|
|
118
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Box, {
|
|
119
|
+
width: "100%",
|
|
120
|
+
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.ErrorBoundary, {
|
|
121
|
+
FallbackComponent: _components.ErrorAlert,
|
|
122
|
+
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_DashboardStickyToolbar.DashboardStickyToolbar, {
|
|
123
|
+
initialVariableIsSticky: initialVariableIsSticky,
|
|
124
|
+
sx: {
|
|
125
|
+
backgroundColor: ({ palette })=>palette.background.default
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
}),
|
|
130
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Stack, {
|
|
131
|
+
direction: "row",
|
|
132
|
+
ml: "auto",
|
|
133
|
+
flexWrap: "wrap",
|
|
134
|
+
justifyContent: "end",
|
|
135
|
+
children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
|
|
136
|
+
direction: "row",
|
|
137
|
+
spacing: 1,
|
|
138
|
+
mt: 1,
|
|
139
|
+
ml: 1,
|
|
140
|
+
children: [
|
|
141
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_pluginsystem.TimeRangeControls, {}),
|
|
142
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_DownloadButton.DownloadButton, {}),
|
|
143
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_EditJsonButton.EditJsonButton, {
|
|
144
|
+
isReadonly: !isEditMode
|
|
145
|
+
})
|
|
146
|
+
]
|
|
147
|
+
})
|
|
159
148
|
})
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
149
|
+
]
|
|
150
|
+
})
|
|
151
|
+
]
|
|
152
|
+
})
|
|
164
153
|
});
|
|
165
154
|
};
|
|
@@ -33,8 +33,41 @@ function _interop_require_default(obj) {
|
|
|
33
33
|
default: obj
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
// Function to extract project name from URL (kept as fallback)
|
|
37
|
+
const extractProjectNameFromUrl = ()=>{
|
|
38
|
+
try {
|
|
39
|
+
if (process.env.NODE_ENV === 'test') {
|
|
40
|
+
return 'test-project';
|
|
41
|
+
}
|
|
42
|
+
if (typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) {
|
|
43
|
+
const urlPath = window.location.pathname;
|
|
44
|
+
if (urlPath === '/' || urlPath === '') {
|
|
45
|
+
return 'dev-project';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const urlPath = window.location.pathname;
|
|
49
|
+
// Split the path and look for the project name after "/projects/"
|
|
50
|
+
const pathSegments = urlPath.split('/').filter((segment)=>segment.length > 0);
|
|
51
|
+
const projectsIndex = pathSegments.findIndex((segment)=>segment === 'projects');
|
|
52
|
+
if (projectsIndex !== -1 && projectsIndex + 1 < pathSegments.length) {
|
|
53
|
+
const projectName = pathSegments[projectsIndex + 1];
|
|
54
|
+
if (projectName && projectName.trim().length > 0) {
|
|
55
|
+
return projectName;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Fallback: try to extract from URL parameters
|
|
59
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
60
|
+
const projectParam = urlParams.get('project');
|
|
61
|
+
if (projectParam && projectParam.trim().length > 0) {
|
|
62
|
+
return projectParam;
|
|
63
|
+
}
|
|
64
|
+
return 'unknown-project';
|
|
65
|
+
} catch {
|
|
66
|
+
return 'unknown-project';
|
|
67
|
+
}
|
|
68
|
+
};
|
|
36
69
|
const Panel = /*#__PURE__*/ (0, _react.memo)(function Panel(props) {
|
|
37
|
-
const { definition, readHandlers, editHandlers, onMouseEnter, onMouseLeave, sx, panelOptions, panelGroupItemId, ...others } = props;
|
|
70
|
+
const { definition, readHandlers, editHandlers, onMouseEnter, onMouseLeave, sx, panelOptions, panelGroupItemId, projectName: providedProjectName, ...others } = props;
|
|
38
71
|
// Make sure we have an ID we can use for aria attributes
|
|
39
72
|
const generatedPanelId = (0, _components.useId)('Panel');
|
|
40
73
|
const headerId = `${generatedPanelId}-header`;
|
|
@@ -54,6 +87,29 @@ const Panel = /*#__PURE__*/ (0, _react.memo)(function Panel(props) {
|
|
|
54
87
|
]);
|
|
55
88
|
const chartsTheme = (0, _components.useChartsTheme)();
|
|
56
89
|
const { queryResults } = (0, _pluginsystem.useDataQueriesContext)();
|
|
90
|
+
// Use provided project name or extract from URL as fallback
|
|
91
|
+
const projectName = (0, _react.useMemo)(()=>{
|
|
92
|
+
return providedProjectName || extractProjectNameFromUrl();
|
|
93
|
+
}, [
|
|
94
|
+
providedProjectName
|
|
95
|
+
]);
|
|
96
|
+
const panelPropsForActions = (0, _react.useMemo)(()=>{
|
|
97
|
+
return {
|
|
98
|
+
spec: definition.spec.plugin.spec,
|
|
99
|
+
queryResults: queryResults.map((query)=>({
|
|
100
|
+
definition: query.definition,
|
|
101
|
+
data: query.data
|
|
102
|
+
})),
|
|
103
|
+
contentDimensions,
|
|
104
|
+
definition,
|
|
105
|
+
projectName
|
|
106
|
+
};
|
|
107
|
+
}, [
|
|
108
|
+
definition,
|
|
109
|
+
contentDimensions,
|
|
110
|
+
queryResults,
|
|
111
|
+
projectName
|
|
112
|
+
]);
|
|
57
113
|
const handleMouseEnter = (e)=>{
|
|
58
114
|
onMouseEnter?.(e);
|
|
59
115
|
};
|
|
@@ -88,9 +144,12 @@ const Panel = /*#__PURE__*/ (0, _react.memo)(function Panel(props) {
|
|
|
88
144
|
title: definition.spec.display.name,
|
|
89
145
|
description: definition.spec.display.description,
|
|
90
146
|
queryResults: queryResults,
|
|
147
|
+
panelPluginKind: definition.spec.plugin.kind,
|
|
91
148
|
readHandlers: readHandlers,
|
|
92
149
|
editHandlers: editHandlers,
|
|
93
150
|
links: definition.spec.links,
|
|
151
|
+
projectName: projectName,
|
|
152
|
+
panelProps: panelPropsForActions,
|
|
94
153
|
sx: {
|
|
95
154
|
paddingX: `${chartsTheme.container.padding.default}px`
|
|
96
155
|
}
|
|
@@ -24,6 +24,7 @@ const _jsxruntime = require("react/jsx-runtime");
|
|
|
24
24
|
const _material = require("@mui/material");
|
|
25
25
|
const _react = require("react");
|
|
26
26
|
const _components = require("@perses-dev/components");
|
|
27
|
+
const _pluginsystem = require("@perses-dev/plugin-system");
|
|
27
28
|
const _ArrowCollapse = /*#__PURE__*/ _interop_require_default(require("mdi-material-ui/ArrowCollapse"));
|
|
28
29
|
const _ArrowExpand = /*#__PURE__*/ _interop_require_default(require("mdi-material-ui/ArrowExpand"));
|
|
29
30
|
const _PencilOutline = /*#__PURE__*/ _interop_require_default(require("mdi-material-ui/PencilOutline"));
|
|
@@ -47,7 +48,72 @@ const ConditionalBox = (0, _material.styled)(_material.Box)({
|
|
|
47
48
|
flexGrow: 1,
|
|
48
49
|
justifyContent: 'flex-end'
|
|
49
50
|
});
|
|
50
|
-
const PanelActions = ({ editHandlers, readHandlers, extra, title, description, descriptionTooltipId, links, queryResults })=>{
|
|
51
|
+
const PanelActions = ({ editHandlers, readHandlers, extra, title, description, descriptionTooltipId, links, queryResults, panelPluginKind, projectName: _propsProjectName, panelProps })=>{
|
|
52
|
+
const { isFetching, errors } = (0, _pluginsystem.useDataQueriesContext)();
|
|
53
|
+
const { getPlugin } = (0, _pluginsystem.usePluginRegistry)();
|
|
54
|
+
const [pluginActions, setPluginActions] = (0, _react.useState)([]);
|
|
55
|
+
(0, _react.useEffect)(()=>{
|
|
56
|
+
let cancelled = false;
|
|
57
|
+
const loadPluginActions = async ()=>{
|
|
58
|
+
if (!panelPluginKind || !panelProps) {
|
|
59
|
+
if (!cancelled) {
|
|
60
|
+
setPluginActions([]);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
// Add defensive check for getPlugin availability
|
|
66
|
+
if (!getPlugin || typeof getPlugin !== 'function') {
|
|
67
|
+
if (!cancelled) {
|
|
68
|
+
setPluginActions([]);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const plugin = await getPlugin('Panel', panelPluginKind);
|
|
73
|
+
if (cancelled) return;
|
|
74
|
+
// More defensive checking for plugin and actions
|
|
75
|
+
if (!plugin || typeof plugin !== 'object' || !plugin.actions || !Array.isArray(plugin.actions) || plugin.actions.length === 0) {
|
|
76
|
+
if (!cancelled) {
|
|
77
|
+
setPluginActions([]);
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Render plugin actions in header location
|
|
82
|
+
const headerActions = plugin.actions.filter((action)=>!action.location || action.location === 'header').map((action, index)=>{
|
|
83
|
+
const ActionComponent = action.component;
|
|
84
|
+
try {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
return /*#__PURE__*/ (0, _jsxruntime.jsx)(ActionComponent, {
|
|
87
|
+
...panelProps
|
|
88
|
+
}, `plugin-action-${index}`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.warn(`Failed to render plugin action ${index}:`, error);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}).filter((item)=>Boolean(item));
|
|
94
|
+
if (!cancelled) {
|
|
95
|
+
setPluginActions(headerActions);
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (!cancelled) {
|
|
99
|
+
console.warn('Failed to load plugin actions:', error);
|
|
100
|
+
setPluginActions([]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
// Use setTimeout to defer the async operation to the next tick
|
|
105
|
+
const timeoutId = setTimeout(()=>{
|
|
106
|
+
loadPluginActions();
|
|
107
|
+
}, 0);
|
|
108
|
+
return ()=>{
|
|
109
|
+
cancelled = true;
|
|
110
|
+
clearTimeout(timeoutId);
|
|
111
|
+
};
|
|
112
|
+
}, [
|
|
113
|
+
panelPluginKind,
|
|
114
|
+
panelProps,
|
|
115
|
+
getPlugin
|
|
116
|
+
]);
|
|
51
117
|
const descriptionAction = (0, _react.useMemo)(()=>{
|
|
52
118
|
if (description && description.trim().length > 0) {
|
|
53
119
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.InfoTooltip, {
|
|
@@ -78,19 +144,23 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
78
144
|
});
|
|
79
145
|
const extraActions = editHandlers === undefined && extra;
|
|
80
146
|
const queryStateIndicator = (0, _react.useMemo)(()=>{
|
|
81
|
-
const hasData = queryResults.
|
|
82
|
-
const isFetching = queryResults.some((q)=>q.isFetching);
|
|
83
|
-
const queryErrors = queryResults.filter((q)=>q.error);
|
|
147
|
+
const hasData = queryResults && queryResults.series && queryResults.series.length > 0;
|
|
84
148
|
if (isFetching && hasData) {
|
|
85
|
-
// If the panel has no data, the panel content will show the loading overlay.
|
|
86
|
-
// Therefore, show the circular loading indicator only in case the panel doesn't display the loading overlay already.
|
|
87
149
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.CircularProgress, {
|
|
88
150
|
"aria-label": "loading",
|
|
89
151
|
size: "1.125rem"
|
|
90
152
|
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
153
|
+
}
|
|
154
|
+
const validErrors = (errors || []).filter((error)=>error !== null);
|
|
155
|
+
if (validErrors.length > 0) {
|
|
156
|
+
const errorTexts = validErrors.map((e)=>{
|
|
157
|
+
if (typeof e === 'string') return e;
|
|
158
|
+
if (e && typeof e === 'object') {
|
|
159
|
+
const errorObj = e;
|
|
160
|
+
return errorObj.message ?? errorObj.toString?.() ?? 'Unknown error';
|
|
161
|
+
}
|
|
162
|
+
return 'Unknown error';
|
|
163
|
+
}).join('\n');
|
|
94
164
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.InfoTooltip, {
|
|
95
165
|
description: errorTexts,
|
|
96
166
|
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_HeaderIconButton.HeaderIconButton, {
|
|
@@ -103,7 +173,9 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
103
173
|
});
|
|
104
174
|
}
|
|
105
175
|
}, [
|
|
106
|
-
queryResults
|
|
176
|
+
queryResults,
|
|
177
|
+
isFetching,
|
|
178
|
+
errors
|
|
107
179
|
]);
|
|
108
180
|
const readActions = (0, _react.useMemo)(()=>{
|
|
109
181
|
if (readHandlers !== undefined) {
|
|
@@ -128,7 +200,6 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
128
200
|
]);
|
|
129
201
|
const editActions = (0, _react.useMemo)(()=>{
|
|
130
202
|
if (editHandlers !== undefined) {
|
|
131
|
-
// If there are edit handlers, always just show the edit buttons
|
|
132
203
|
return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
|
|
133
204
|
children: [
|
|
134
205
|
/*#__PURE__*/ (0, _jsxruntime.jsx)(_components.InfoTooltip, {
|
|
@@ -151,8 +222,6 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
151
222
|
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_ContentCopy.default, {
|
|
152
223
|
fontSize: "inherit",
|
|
153
224
|
sx: {
|
|
154
|
-
// Shrink this icon a little bit to look more consistent
|
|
155
|
-
// with the other icons in the header.
|
|
156
225
|
transform: 'scale(0.925)'
|
|
157
226
|
}
|
|
158
227
|
})
|
|
@@ -242,6 +311,7 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
242
311
|
editActions
|
|
243
312
|
]
|
|
244
313
|
}),
|
|
314
|
+
pluginActions,
|
|
245
315
|
moveAction
|
|
246
316
|
]
|
|
247
317
|
})
|
|
@@ -269,6 +339,7 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
269
339
|
extraActions,
|
|
270
340
|
" ",
|
|
271
341
|
readActions,
|
|
342
|
+
pluginActions,
|
|
272
343
|
/*#__PURE__*/ (0, _jsxruntime.jsx)(OverflowMenu, {
|
|
273
344
|
title: title,
|
|
274
345
|
children: editActions
|
|
@@ -280,7 +351,6 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
280
351
|
}),
|
|
281
352
|
/*#__PURE__*/ (0, _jsxruntime.jsxs)(ConditionalBox, {
|
|
282
353
|
sx: (theme)=>({
|
|
283
|
-
// flip the logic here; if the browser (or jsdom) does not support container queries, always show all icons
|
|
284
354
|
display: 'flex',
|
|
285
355
|
[theme.containerQueries(_constants.HEADER_ACTIONS_CONTAINER_NAME).down(_constants.HEADER_MEDIUM_WIDTH)]: {
|
|
286
356
|
display: 'none'
|
|
@@ -302,7 +372,7 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
302
372
|
extraActions,
|
|
303
373
|
" ",
|
|
304
374
|
readActions,
|
|
305
|
-
|
|
375
|
+
pluginActions,
|
|
306
376
|
editActions,
|
|
307
377
|
" ",
|
|
308
378
|
moveAction
|
|
@@ -315,7 +385,7 @@ const PanelActions = ({ editHandlers, readHandlers, extra, title, description, d
|
|
|
315
385
|
};
|
|
316
386
|
const OverflowMenu = ({ children, title })=>{
|
|
317
387
|
const [anchorPosition, setAnchorPosition] = (0, _react.useState)();
|
|
318
|
-
// do not show overflow menu if there is no content
|
|
388
|
+
// do not show overflow menu if there is no content
|
|
319
389
|
const hasContent = /*#__PURE__*/ (0, _react.isValidElement)(children) || Array.isArray(children) && children.some(_react.isValidElement);
|
|
320
390
|
if (!hasContent) {
|
|
321
391
|
return undefined;
|
|
@@ -24,13 +24,53 @@ const _jsxruntime = require("react/jsx-runtime");
|
|
|
24
24
|
const _material = require("@mui/material");
|
|
25
25
|
const _components = require("@perses-dev/components");
|
|
26
26
|
const _pluginsystem = require("@perses-dev/plugin-system");
|
|
27
|
+
const _react = require("react");
|
|
27
28
|
const _constants = require("../../constants");
|
|
28
29
|
const _PanelActions = require("./PanelActions");
|
|
29
|
-
function PanelHeader({ id, title: rawTitle, description: rawDescription, links, queryResults, readHandlers, editHandlers, sx, extra, ...rest }) {
|
|
30
|
+
function PanelHeader({ id, title: rawTitle, description: rawDescription, links, queryResults, readHandlers, editHandlers, sx, extra, panelPluginKind, projectName, panelProps, ...rest }) {
|
|
30
31
|
const titleElementId = `${id}-title`;
|
|
31
32
|
const descriptionTooltipId = `${id}-description`;
|
|
32
33
|
const title = (0, _pluginsystem.useReplaceVariablesInString)(rawTitle);
|
|
33
34
|
const description = (0, _pluginsystem.useReplaceVariablesInString)(rawDescription);
|
|
35
|
+
const timeSeriesDataForExport = (0, _react.useMemo)(()=>{
|
|
36
|
+
// Collect all series from all queries
|
|
37
|
+
const allSeries = [];
|
|
38
|
+
let timeRange = undefined;
|
|
39
|
+
let stepMs = undefined;
|
|
40
|
+
let metadata = undefined;
|
|
41
|
+
queryResults.forEach((query)=>{
|
|
42
|
+
if (query.data && 'series' in query.data) {
|
|
43
|
+
const timeSeriesData = query.data;
|
|
44
|
+
// Collect series from this query
|
|
45
|
+
if (timeSeriesData.series && timeSeriesData.series.length > 0) {
|
|
46
|
+
allSeries.push(...timeSeriesData.series);
|
|
47
|
+
// Use the first query's metadata/timeRange/stepMs as the base
|
|
48
|
+
if (!timeRange && timeSeriesData.timeRange) {
|
|
49
|
+
timeRange = timeSeriesData.timeRange;
|
|
50
|
+
}
|
|
51
|
+
if (!stepMs && timeSeriesData.stepMs) {
|
|
52
|
+
stepMs = timeSeriesData.stepMs;
|
|
53
|
+
}
|
|
54
|
+
if (!metadata && timeSeriesData.metadata) {
|
|
55
|
+
metadata = timeSeriesData.metadata;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// If we found series, create a combined TimeSeriesData object
|
|
61
|
+
if (allSeries.length > 0) {
|
|
62
|
+
const combinedData = {
|
|
63
|
+
series: allSeries,
|
|
64
|
+
timeRange,
|
|
65
|
+
stepMs,
|
|
66
|
+
metadata
|
|
67
|
+
};
|
|
68
|
+
return combinedData;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}, [
|
|
72
|
+
queryResults
|
|
73
|
+
]);
|
|
34
74
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.CardHeader, {
|
|
35
75
|
id: id,
|
|
36
76
|
component: "header",
|
|
@@ -59,10 +99,14 @@ function PanelHeader({ id, title: rawTitle, description: rawDescription, links,
|
|
|
59
99
|
description: description,
|
|
60
100
|
descriptionTooltipId: descriptionTooltipId,
|
|
61
101
|
links: links,
|
|
62
|
-
queryResults:
|
|
102
|
+
queryResults: timeSeriesDataForExport,
|
|
103
|
+
panelPluginKind: panelPluginKind,
|
|
63
104
|
readHandlers: readHandlers,
|
|
64
105
|
editHandlers: editHandlers,
|
|
65
|
-
extra: extra
|
|
106
|
+
extra: extra,
|
|
107
|
+
projectName: projectName,
|
|
108
|
+
// ========== ADDED: Pass panel props for actions ==========
|
|
109
|
+
panelProps: panelProps
|
|
66
110
|
})
|
|
67
111
|
]
|
|
68
112
|
}),
|
|
@@ -31,11 +31,8 @@ const SaveChangesConfirmationDialog = ()=>{
|
|
|
31
31
|
const { saveChangesConfirmationDialog: dialog } = (0, _context.useSaveChangesConfirmationDialog)();
|
|
32
32
|
const isSavedDurationModified = dialog?.isSavedDurationModified ?? true;
|
|
33
33
|
const isSavedVariableModified = dialog?.isSavedVariableModified ?? true;
|
|
34
|
-
const isSavedTimeZoneModified = dialog?.isSavedTimeZoneModified ?? true;
|
|
35
34
|
const [saveDefaultTimeRange, setSaveDefaultTimeRange] = (0, _react.useState)(isSavedDurationModified);
|
|
36
35
|
const [saveDefaultVariables, setSaveDefaultVariables] = (0, _react.useState)(isSavedVariableModified);
|
|
37
|
-
const [saveDefaultTimeZone, setSaveDefaultTimeZone] = (0, _react.useState)(isSavedTimeZoneModified);
|
|
38
|
-
const { timeZone } = (0, _pluginsystem.useTimeZone)();
|
|
39
36
|
const { getSavedVariablesStatus } = (0, _context.useVariableDefinitionActions)();
|
|
40
37
|
const { modifiedVariableNames } = getSavedVariablesStatus();
|
|
41
38
|
const isOpen = dialog !== undefined;
|
|
@@ -43,7 +40,6 @@ const SaveChangesConfirmationDialog = ()=>{
|
|
|
43
40
|
const currentTimeRangeText = (0, _core.isRelativeTimeRange)(timeRange) ? `(Last ${timeRange.pastDuration})` : '(Absolute time ranges can not be saved)';
|
|
44
41
|
const saveTimeRangeText = `Save current time range as new default ${currentTimeRangeText}`;
|
|
45
42
|
const saveVariablesText = `Save current variable values as new default (${modifiedVariableNames.length > 0 ? modifiedVariableNames.join(', ') : 'No modified variables'})`;
|
|
46
|
-
const saveTimeZoneText = `Save time zone as "${timeZone}" time`;
|
|
47
43
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.Dialog, {
|
|
48
44
|
open: isOpen,
|
|
49
45
|
children: dialog !== undefined && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
|
|
@@ -75,14 +71,6 @@ const SaveChangesConfirmationDialog = ()=>{
|
|
|
75
71
|
onChange: (e)=>setSaveDefaultVariables(e.target.checked)
|
|
76
72
|
}),
|
|
77
73
|
label: saveVariablesText
|
|
78
|
-
}),
|
|
79
|
-
/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.FormControlLabel, {
|
|
80
|
-
control: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Checkbox, {
|
|
81
|
-
disabled: !isSavedTimeZoneModified,
|
|
82
|
-
checked: saveDefaultTimeZone && isSavedTimeZoneModified,
|
|
83
|
-
onChange: (e)=>setSaveDefaultTimeZone(e.target.checked)
|
|
84
|
-
}),
|
|
85
|
-
label: saveTimeZoneText
|
|
86
74
|
})
|
|
87
75
|
]
|
|
88
76
|
})
|
|
@@ -92,7 +80,7 @@ const SaveChangesConfirmationDialog = ()=>{
|
|
|
92
80
|
children: [
|
|
93
81
|
/*#__PURE__*/ (0, _jsxruntime.jsx)(_components.Dialog.PrimaryButton, {
|
|
94
82
|
onClick: ()=>{
|
|
95
|
-
return dialog.onSaveChanges(saveDefaultTimeRange, saveDefaultVariables
|
|
83
|
+
return dialog.onSaveChanges(saveDefaultTimeRange, saveDefaultVariables);
|
|
96
84
|
},
|
|
97
85
|
children: "Save Changes"
|
|
98
86
|
}),
|
|
@@ -34,17 +34,14 @@ const SaveDashboardButton = ({ onSave, isDisabled, variant = 'contained' })=>{
|
|
|
34
34
|
const { timeRange } = (0, _pluginsystem.useTimeRange)();
|
|
35
35
|
const { setEditMode } = (0, _context.useEditMode)();
|
|
36
36
|
const { openSaveChangesConfirmationDialog, closeSaveChangesConfirmationDialog } = (0, _context.useSaveChangesConfirmationDialog)();
|
|
37
|
-
const { timeZone: currentTimeZone } = (0, _pluginsystem.useTimeZone)();
|
|
38
37
|
const onSaveButtonClick = ()=>{
|
|
39
38
|
const isSavedDurationModified = (0, _core.isRelativeTimeRange)(timeRange) && dashboard.spec.duration !== timeRange.pastDuration;
|
|
40
|
-
const isSavedTimeZoneModified = dashboard.spec.timeZone !== currentTimeZone;
|
|
41
39
|
// Save dashboard
|
|
42
40
|
// - if active timeRange from plugin-system is relative and different from currently saved
|
|
43
41
|
// - or if the saved variables are different from currently saved
|
|
44
|
-
|
|
45
|
-
if (isSavedDurationModified || isSavedVariableModified || isSavedTimeZoneModified) {
|
|
42
|
+
if (isSavedDurationModified || isSavedVariableModified) {
|
|
46
43
|
openSaveChangesConfirmationDialog({
|
|
47
|
-
onSaveChanges: (saveDefaultTimeRange, saveDefaultVariables
|
|
44
|
+
onSaveChanges: (saveDefaultTimeRange, saveDefaultVariables)=>{
|
|
48
45
|
if ((0, _core.isRelativeTimeRange)(timeRange) && saveDefaultTimeRange === true) {
|
|
49
46
|
dashboard.spec.duration = timeRange.pastDuration;
|
|
50
47
|
}
|
|
@@ -52,9 +49,6 @@ const SaveDashboardButton = ({ onSave, isDisabled, variant = 'contained' })=>{
|
|
|
52
49
|
const variables = setVariableDefaultValues();
|
|
53
50
|
dashboard.spec.variables = variables;
|
|
54
51
|
}
|
|
55
|
-
if (isSavedTimeZone) {
|
|
56
|
-
dashboard.spec.timeZone = currentTimeZone;
|
|
57
|
-
}
|
|
58
52
|
setDashboard(dashboard);
|
|
59
53
|
saveDashboard();
|
|
60
54
|
},
|
|
@@ -62,8 +56,7 @@ const SaveDashboardButton = ({ onSave, isDisabled, variant = 'contained' })=>{
|
|
|
62
56
|
closeSaveChangesConfirmationDialog();
|
|
63
57
|
},
|
|
64
58
|
isSavedDurationModified,
|
|
65
|
-
isSavedVariableModified
|
|
66
|
-
isSavedTimeZoneModified
|
|
59
|
+
isSavedVariableModified
|
|
67
60
|
});
|
|
68
61
|
} else {
|
|
69
62
|
saveDashboard();
|