diff --git a/src/card/Card.tsx b/src/card/Card.tsx index 2a7753c6e..ef10d4782 100644 --- a/src/card/Card.tsx +++ b/src/card/Card.tsx @@ -39,6 +39,7 @@ const NeoCard = ({ extensions, // A set of enabled extensions. globalParameters, // Query parameters that are globally set for the entire dashboard. dashboardSettings, // Dictionary of settings for the entire dashboard. + enableExecuteButtonForIds, // Reports will have save buttons to execute cypher queries onRemovePressed, // action to take when the card is removed. (passed from parent) onClonePressed, // action to take when user presses the clone button onReportHelpButtonPressed, // action to take when someone clicks the 'help' button in the report settings. @@ -131,6 +132,7 @@ const NeoCard = ({ settingsOpen={settingsOpen} editable={editable} dashboardSettings={dashboardSettings} + enableExecuteButtonForIds={enableExecuteButtonForIds} extensions={extensions} settings={report.settings ? report.settings : {}} updateReportSetting={(name, value) => onReportSettingUpdate(id, name, value)} diff --git a/src/card/view/CardView.tsx b/src/card/view/CardView.tsx index 452a24aae..cca5b626a 100644 --- a/src/card/view/CardView.tsx +++ b/src/card/view/CardView.tsx @@ -2,16 +2,18 @@ import React, { useEffect, useState } from 'react'; import { ReportItemContainer } from '../CardStyle'; import NeoCardViewHeader from './CardViewHeader'; import NeoCardViewFooter from './CardViewFooter'; -import { CardContent } from '@mui/material'; +import { CardContent, Fab, Stack } from '@mui/material'; import NeoCodeEditorComponent from '../../component/editor/CodeEditorComponent'; import { CARD_FOOTER_HEIGHT, CARD_HEADER_HEIGHT } from '../../config/CardConfig'; import { getReportTypes } from '../../extensions/ExtensionUtils'; import NeoCodeViewerComponent from '../../component/editor/CodeViewerComponent'; import { NeoReportWrapper } from '../../report/ReportWrapper'; import { identifyStyleRuleParameters } from '../../extensions/styling/StyleRuleEvaluator'; -import { IconButton } from '@neo4j-ndl/react'; -import { PlayCircleIconSolid } from '@neo4j-ndl/react/icons'; +import { IconButton, Typography } from '@neo4j-ndl/react'; +import { PlayCircleIconSolid, PlayIconOutline } from '@neo4j-ndl/react/icons'; import { extensionEnabled } from '../../utils/ReportUtils'; +import { PlayArrowOutlined } from '@mui/icons-material'; +import { checkParametersNameInGlobalParameter, extractAllParameterNames } from '../../utils/parameterUtils'; import { objMerge } from '../../utils/ObjectManipulation'; import { REPORT_TYPES } from '../../config/ReportConfig'; @@ -31,6 +33,7 @@ const NeoCardView = ({ type, selection, dashboardSettings, + enableExecuteButtonForIds, settings, updateReportSetting, createNotification, @@ -150,6 +153,11 @@ const NeoCardView = ({ if (!settingsOpen) { setLastRunTimestamp(Date.now()); } + + // Resets the report with save button + if (enableExecuteButtonForIds.map((report) => report.id).includes(id)) { + setActive(false); + } }, [JSON.stringify(localParameters)]); useEffect(() => { @@ -180,6 +188,68 @@ const NeoCardView = ({ : `${reportHeight}px`, overflow: 'auto', }; + + const isParametersDefined = (cypherQuery: string) => { + const parameterNames = extractAllParameterNames(cypherQuery); + if (globalParameters) { + return checkParametersNameInGlobalParameter(parameterNames, globalParameters); + } + return false; + }; + + const executeButton = ( +
+ + { + setActive(true); + }} + size='large' + style={{ + color: '#ffffff', + backgroundColor: 'green', + }} + > + + + + {settings.executeButtonName ?? 'Execute'} + + +
+ ); + + const cypherQueryEditor = ( + <> + { + setActive(true); + }} + clean + size='small' + > + + + {}} + placeholder={'No query specified...'} + /> + + ); + const reportContent = ( {active ? ( @@ -206,33 +276,10 @@ const NeoCardView = ({ queryTimeLimit={dashboardSettings.queryTimeLimit ? dashboardSettings.queryTimeLimit : 20} setFields={onFieldsUpdate} /> + ) : settings.hideQueryEditorInAutoRunOnMode ? ( + executeButton ) : ( - <> - { - setActive(true); - }} - clean - > - - - {}} - placeholder={'No query specified...'} - /> - + cypherQueryEditor )} ); diff --git a/src/config/ReportConfig.tsx b/src/config/ReportConfig.tsx index 97b6bc913..a9e5ba524 100644 --- a/src/config/ReportConfig.tsx +++ b/src/config/ReportConfig.tsx @@ -25,6 +25,23 @@ export const RUN_QUERY_DELAY_MS = 300; // The default number of rows to process in a visualization. export const DEFAULT_ROW_LIMIT = 100; +const hideQueryEditorInAutoRunOnMode = { + hideQueryEditorInAutoRunOnMode: { + label: 'Hide query editor on auto run on mode', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, +}; + +const executeButtonName = { + executeButtonName: { + label: 'Execute Button Name', + type: SELECTION_TYPES.TEXT, + default: 'Execute', + }, +}; + // A dictionary of available reports (visualizations). const _REPORT_TYPES = { table: { @@ -69,6 +86,36 @@ const _REPORT_TYPES = { values: [true, false], default: false, }, + refreshButtonEnabled: { + label: 'Refreshable', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + fullscreenEnabled: { + label: 'Fullscreen enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + downloadImageEnabled: { + label: 'Download Image enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + ...hideQueryEditorInAutoRunOnMode, + refreshRate: { + label: 'Refresh rate (seconds)', + type: SELECTION_TYPES.NUMBER, + default: '0 (No refresh)', + }, }, }, graph: { @@ -242,6 +289,13 @@ const _REPORT_TYPES = { values: [true, false], default: true, }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + ...hideQueryEditorInAutoRunOnMode, iconStyle: { label: 'Node Label images', type: SELECTION_TYPES.TEXT, @@ -254,6 +308,26 @@ const _REPORT_TYPES = { values: [true, false], default: false, }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', + }, + customTablePropertiesOfModal: { + label: 'Customized Ordering and Hide Features Of Attributes In Detailed Modal', + type: SELECTION_TYPES.DICTIONARY, + }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + pageIdAndParameterName: { + label: '::', + type: SELECTION_TYPES.TEXT, + }, + ...executeButtonName, }, }, bar: { @@ -438,11 +512,24 @@ const _REPORT_TYPES = { values: ['Horizontal', 'Vertical'], default: 'Vertical', }, - padding: { - label: 'Padding', + ...hideQueryEditorInAutoRunOnMode, + refreshRate: { + label: 'Refresh rate (seconds)', type: SELECTION_TYPES.NUMBER, - default: 0.25, + default: '0 (No refresh)', + }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + ...executeButtonName, }, }, pie: { @@ -573,6 +660,48 @@ const _REPORT_TYPES = { type: SELECTION_TYPES.NUMBER, default: 50, }, + refreshButtonEnabled: { + label: 'Refreshable', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + fullscreenEnabled: { + label: 'Fullscreen enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + downloadImageEnabled: { + label: 'Download Image enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + ...hideQueryEditorInAutoRunOnMode, + refreshRate: { + label: 'Refresh rate (seconds)', + type: SELECTION_TYPES.NUMBER, + default: '0 (No refresh)', + }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', + }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + ...executeButtonName, }, }, line: { @@ -721,6 +850,48 @@ const _REPORT_TYPES = { values: [true, false], default: false, }, + refreshButtonEnabled: { + label: 'Refreshable', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + fullscreenEnabled: { + label: 'Fullscreen enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + downloadImageEnabled: { + label: 'Download Image enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + ...hideQueryEditorInAutoRunOnMode, + refreshRate: { + label: 'Refresh rate (seconds)', + type: SELECTION_TYPES.NUMBER, + default: '0 (No refresh)', + }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', + }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + ...executeButtonName, }, }, // TODO - move to advanced visualization. @@ -867,6 +1038,46 @@ const _REPORT_TYPES = { // values: [true, false], // default: false, // }, + // refreshButtonEnabled: { + // label: 'Refreshable', + // type: SELECTION_TYPES.LIST, + // values: [true, false], + // default: false, + // }, + // fullscreenEnabled: { + // label: 'Fullscreen enabled', + // type: SELECTION_TYPES.LIST, + // values: [true, false], + // default: false, + // }, + // downloadImageEnabled: { + // label: 'Download Image enabled', + // type: SELECTION_TYPES.LIST, + // values: [true, false], + // default: false, + // }, + // autorun: { + // label: 'Auto-run query', + // type: SELECTION_TYPES.LIST, + // values: [true, false], + // default: true, + // }, + // ...hideQueryEditorInAutoRunOnMode, + // refreshRate: { + // label: 'Refresh rate (seconds)', + // type: SELECTION_TYPES.NUMBER, + // default: '0 (No refresh)', + // }, + // description: { + // label: 'Report Description', + // type: SELECTION_TYPES.MULTILINE_TEXT, + // default: 'Enter markdown here...', + // }, + // executeButtonName: { + // label: 'Execute Button Name', + // type: SELECTION_TYPES.TEXT, + // default: 'Execute', + // }, // }, // }, map: { @@ -965,6 +1176,30 @@ const _REPORT_TYPES = { values: [true, false], default: false, }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + hideQueryEditorInAutoRunOnMode: { + label: 'Hide query editor on auto run on mode', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', + }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + ...executeButtonName, }, }, value: { @@ -1007,6 +1242,48 @@ const _REPORT_TYPES = { values: ['bottom', 'middle', 'top'], default: 'top', }, + refreshButtonEnabled: { + label: 'Refreshable', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + fullscreenEnabled: { + label: 'Fullscreen enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + downloadImageEnabled: { + label: 'Download Image enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + ...hideQueryEditorInAutoRunOnMode, + refreshRate: { + label: 'Refresh rate (seconds)', + type: SELECTION_TYPES.NUMBER, + default: '0 (No refresh)', + }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', + }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + ...executeButtonName, }, }, json: { @@ -1022,6 +1299,48 @@ const _REPORT_TYPES = { values: ['json', 'yml'], default: 'json', }, + refreshButtonEnabled: { + label: 'Refreshable', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + fullscreenEnabled: { + label: 'Fullscreen enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + downloadImageEnabled: { + label: 'Download Image enabled', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + autorun: { + label: 'Auto-run query', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: true, + }, + ...hideQueryEditorInAutoRunOnMode, + refreshRate: { + label: 'Refresh rate (seconds)', + type: SELECTION_TYPES.NUMBER, + default: '0 (No refresh)', + }, + description: { + label: 'Report Description', + type: SELECTION_TYPES.MULTILINE_TEXT, + default: 'Enter markdown here...', + }, + minimizable: { + label: 'Minimize Button', + type: SELECTION_TYPES.LIST, + values: [true, false], + default: false, + }, + ...executeButtonName, }, }, select: { diff --git a/src/page/Page.tsx b/src/page/Page.tsx index ec487fe48..471376c6e 100644 --- a/src/page/Page.tsx +++ b/src/page/Page.tsx @@ -47,6 +47,9 @@ export const NeoPage = ({ const [lastElement, setLastElement] = React.useState(
); const [animated, setAnimated] = React.useState(false); // To turn off animations when cards are dragged around. + // If hideQueryEditorInAutoRunOnMode is true and autorun option is false + const enableExecuteButtonForIds = reports.filter((report: any) => report.settings?.hideQueryEditorInAutoRunOnMode); + const availableHandles = () => { if (dashboardSettings.resizing && dashboardSettings.resizing == 'all') { return ['s', 'w', 'e', 'sw', 'se']; @@ -197,6 +200,7 @@ export const NeoPage = ({ id={id} key={getReportKey(pagenumber, id)} dashboardSettings={dashboardSettings} + enableExecuteButtonForIds={enableExecuteButtonForIds} onRemovePressed={onRemovePressed} onClonePressed={(id) => { const { x, y } = getAddCardButtonPosition(); diff --git a/src/utils/parameterUtils.ts b/src/utils/parameterUtils.ts new file mode 100644 index 000000000..d2ce7b0a0 --- /dev/null +++ b/src/utils/parameterUtils.ts @@ -0,0 +1,22 @@ +export const extractAllParameterNames = (cypherQuery) => { + // A regular expression pattern to match parameter names following '$' + const pattern = /\$([A-Za-z_]\w*)/g; + + const parameterNames: string[] = []; + let match: any; + + while ((match = pattern.exec(cypherQuery)) !== null) { + parameterNames.push(match[1]); + } + + return parameterNames; +} + +export const checkParametersNameInGlobalParameter = (parameterNames: string[], globalParameterNames: any,) => { + for (const key of parameterNames) { + if (!globalParameterNames[key] || globalParameterNames[key].trim() === '') { + return true; + } + } + return false; +} \ No newline at end of file