diff --git a/.github/workflows/develop-deployment.yml b/.github/workflows/develop-deployment.yml index 503a1e92c..b8d2381be 100644 --- a/.github/workflows/develop-deployment.yml +++ b/.github/workflows/develop-deployment.yml @@ -31,16 +31,6 @@ jobs: build: yarn run build start: yarn run dev wait-on: 'http://localhost:3000' - deploy-docs: - needs: build-test - runs-on: ubuntu-latest - steps: - - name: Trigger Developer Event - uses: peter-evans/repository-dispatch@main - with: - token: ${{ secrets.DOCS_REFRESH_TOKEN }} - repository: neo4j-documentation/docs-refresh - event-type: labs build-s3: needs: build-test runs-on: ubuntu-latest @@ -63,23 +53,3 @@ jobs: aws-region: us-west-1 - run: curl ${{ secrets.INDEX_HTML_DEPLOYMENT_URL }} > dist/index.html - run: aws s3 rm s3://neodash-test.graphapp.io/ --recursive && aws s3 sync dist s3://neodash-test.graphapp.io/ --acl public-read - deploy-gallery: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: cd gallery && yarn install - - run: cd gallery && CI=false yarn run build - - name: Set AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-west-1 - - run: aws s3 rm s3://neodash-gallery.graphapp.io/ --recursive && aws s3 sync gallery/build s3://neodash-gallery.graphapp.io/ --acl public-read diff --git a/.github/workflows/master-deployment.yml b/.github/workflows/master-deployment.yml index a00943219..6621d9527 100644 --- a/.github/workflows/master-deployment.yml +++ b/.github/workflows/master-deployment.yml @@ -78,7 +78,7 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.1.9 + tags: ${{ secrets.DOCKER_HUB_USERNAME }}/neodash:latest,${{ secrets.DOCKER_HUB_USERNAME }}/neodash:2.1.10 build-npm: needs: build-test runs-on: ubuntu-latest @@ -116,3 +116,33 @@ jobs: - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - name: Publish package to NPM 📦 run: npm publish --access public neodash*.tgz + deploy-gallery: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: cd gallery && yarn install + - run: cd gallery && CI=false yarn run build + - name: Set AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-1 + - run: aws s3 rm s3://neodash-gallery.graphapp.io/ --recursive && aws s3 sync gallery/build s3://neodash-gallery.graphapp.io/ --acl public-read + deploy-docs: + needs: build-test + runs-on: ubuntu-latest + steps: + - name: Trigger Developer Event + uses: peter-evans/repository-dispatch@main + with: + token: ${{ secrets.DOCS_REFRESH_TOKEN }} + repository: neo4j-documentation/docs-refresh + event-type: labs \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7ee58368a..a12904eac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,4 +38,4 @@ RUN chown -R nginx:nginx /usr/share/nginx/html/ USER nginx EXPOSE 5005 HEALTHCHECK cmd curl --fail http://localhost:5005 || exit 1 -LABEL version="2.1.9" +LABEL version="2.1.10" diff --git a/changelog.md b/changelog.md index 4ddc15ac9..003df330e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ +## NeoDash 2.1.10 +This is a minor update which adds some operational/styling improvements, and a bug fix for line charts. + +Changes: +- Added customizable label positions for bar charts. +- Fixed bug where datetimes were not handled correctly by line charts. (https://github.com/neo4j-labs/neodash/issues/243) +- Added **session parameters**, set automatically and available to Cypher queries ([Documentation](https://neo4j.com/labs/neodash/2.1/user-guide/reports/)). +- Added option to restore debug reports in recovery mode. +- Added option to share dashboards from self-hosted deployments. + ## NeoDash 2.1.8 & 2.1.9 New features: - Added the [Dashboard Gallery](https://neodash-gallery.graphapp.io), a live gallery of example NeoDash dashboards. diff --git a/docs/modules/ROOT/pages/user-guide/dashboards.adoc b/docs/modules/ROOT/pages/user-guide/dashboards.adoc index b77d9215b..5392f11eb 100644 --- a/docs/modules/ROOT/pages/user-guide/dashboards.adoc +++ b/docs/modules/ROOT/pages/user-guide/dashboards.adoc @@ -82,6 +82,8 @@ When using Neo4j multi-database, you will be given the choice of which database to save the dashboard in. 3. Copy-paste the JSON file directly. +> Keep in mind that your currently active dashboard is stored in the browser cache. If you clear your cache (cookies), the dashboard is gone. + === Load a Dashboard Just like in the save screen, a dashboard can be loaded in one of three diff --git a/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc b/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc index 68783a9f2..24f431362 100644 --- a/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc +++ b/docs/modules/ROOT/pages/user-guide/reports/bar-chart.adoc @@ -67,6 +67,13 @@ groups returned by the Cypher query. |Show Values on Bars |on/off |off |If enabled, shows the category value inside the respective bar. +|Skip label on width (px) |number |0 |Skip label if bar width is lower than provided value, ignored if 0. + +|Skip label on height (px) |number |0 |Skip label if bar height is lower than provided value, ignored if 0. + +|Custom label position |off/top/bottom |off | Allow user to place label out of the bar. This will override any other +label configuration. + |Label Rotation (degrees) |number |45 |the angle at which the bar labels are rotated. diff --git a/docs/modules/ROOT/pages/user-guide/reports/index.adoc b/docs/modules/ROOT/pages/user-guide/reports/index.adoc index c872f8fdb..843852a65 100644 --- a/docs/modules/ROOT/pages/user-guide/reports/index.adoc +++ b/docs/modules/ROOT/pages/user-guide/reports/index.adoc @@ -99,6 +99,20 @@ Ultimately, it is important to understand that the order of the rules is important. If a node matches multiple rules, the first rule that matches will be used. If no rules are matched, the default style will be used. +== Parameters +... + +Parameters can be set in a dashboard by using a link:parameter-select[Parameter Select] report. Set parameters are then available in any Cypher query across the dashboard. + +In addition, **session parameters** are available based on the currently active database connection. + +|=== +|Parameter | Description +| $session_uri | The URI of the current active database connection. +| $session_database | The Neo4j database that was connected to when the user logged in. +| $session_username | The username used to authenticate to Neo4j. +|=== + == Report Types To learn more about a specific report type, see one of the following diff --git a/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc b/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc index 1e7c64c4e..dbb3e6611 100644 --- a/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc +++ b/docs/modules/ROOT/pages/user-guide/reports/line-chart.adoc @@ -53,7 +53,7 @@ Colors are assigned automatically to the different fields selected in the report footer. |X Scale |List |linear |How to scale the values on the x-axis. Can be -either linear or logarithmic. +either linear, logarithmic or point. Use point for categorical data. |Y Scale |List |linear |How to scale the values on the y-axis. Can be either linear or logarithmic. diff --git a/gallery/src/App.tsx b/gallery/src/App.tsx index fa3e6ab56..323c18e11 100644 --- a/gallery/src/App.tsx +++ b/gallery/src/App.tsx @@ -59,7 +59,7 @@ function App() { filteredList.map(item => { return
-

{item['language']}

+

{item['language']}{item['logo'] ? : <>}

{item['title']}

{item['description']} diff --git a/package.json b/package.json index 4d9e80d7f..b89fa9fc8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "neodash", - "version": "2.1.9", + "version": "2.1.10", "description": "NeoDash - Neo4j Dashboard Builder", "neo4jDesktop": { "apiVersion": "^1.2.0" diff --git a/release-notes.md b/release-notes.md index bfb179e9c..0798000c8 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,15 +1,9 @@ -## NeoDash 2.1.8 & 2.1.9 -New features: -- Added the [Dashboard Gallery](https://neodash-gallery.graphapp.io), a live gallery of example NeoDash dashboards. -- Added **Gauge Charts**, a contribution of the [BlueHound](https://github.com/zeronetworks/BlueHound) fork. -- Updated testing pipeline to work as an independent procedure. -- Added option to select a different Neo4j database for each report. ([#188](https://github.com/neo4j-labs/neodash/issues/118)) -- Added **Report Actions**, a neodash extension (available in beta) only on [https://neodash.graphapp.io](https://neodash.graphapp.io). ([#27](https://github.com/neo4j-labs/neodash/issues/27)) - -Bug fixes: -- Fixed issue preventing dashboards to be shared with a non-standard database name. -- Fixed table chart breaking when returning a property called 'id' with a null value. -- Fixed bug not allowing users to select a different database when loading/saving a dashboard. -- **Added error handler for database list race condition in Neo4j Desktop**. +## NeoDash 2.1.10 +This is a minor update which adds some operational/styling improvements, and a bug fix for line charts. -For a complete version history, see the [Changelog](https://github.com/neo4j-labs/neodash/blob/master/changelog.md). +Changes: +- Added customizable label positions for bar charts. +- Fixed bug where datetimes were not handled correctly by line charts. (https://github.com/neo4j-labs/neodash/issues/243) +- Added **session parameters**, set automatically and available to Cypher queries ([Documentation](https://neo4j.com/labs/neodash/2.1/user-guide/reports/)). +- Added option to restore debug reports in recovery mode. +- Added option to share dashboards from self-hosted deployments. \ No newline at end of file diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index 8fc4be2b2..7b38b2c29 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -117,6 +117,13 @@ export const setWaitForSSO = (wait: boolean ) => ({ payload: { wait }, }); +export const SET_SESSION_PARAMETERS = 'APPLICATION/SET_SESSION_PARAMETERS'; +export const setSessionParameters = ( parameters: any ) => ({ + type: SET_SESSION_PARAMETERS, + payload: { parameters }, +}); + + export const SET_DASHBOARD_TO_LOAD_AFTER_CONNECTING = 'APPLICATION/SET_DASHBOARD_TO_LOAD_AFTER_CONNECTING'; export const setDashboardToLoadAfterConnecting = (id: any) => ({ type: SET_DASHBOARD_TO_LOAD_AFTER_CONNECTING, diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 4307b3906..98fa091e4 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -9,6 +9,7 @@ import { SET_DASHBOARD_TO_LOAD_AFTER_CONNECTING, SET_DESKTOP_CONNECTION_PROPERTIES, SET_OLD_DASHBOARD, SET_PARAMETERS_TO_LOAD_AFTER_CONNECTING, SET_REPORT_HELP_MODAL_OPEN, + SET_SESSION_PARAMETERS, SET_SHARE_DETAILS_FROM_URL, SET_SSO_ENABLED, SET_STANDALONE_DASHBOARD_DATEBASE, SET_STANDALONE_ENABLED, SET_STANDALONE_MODE, SET_WAIT_FOR_SSO, SET_WELCOME_SCREEN_OPEN } from "./ApplicationActions"; @@ -101,7 +102,11 @@ export const applicationReducer = (state = initialState, action: { type: any; pa state = update(state, { waitForSSO: wait }) return state; } - + case SET_SESSION_PARAMETERS: { + const { parameters } = payload; + state = update(state, { sessionParameters: parameters }) + return state; + } case SET_STANDALONE_ENABLED: { const { standalone, standaloneProtocol, standaloneHost, standalonePort, standaloneDatabase, standaloneDashboardName, standaloneDashboardDatabase, standaloneDashboardURL, standaloneUsername, standalonePassword } = payload; state = update(state, { diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index 2ca4790a1..29684b4e9 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -5,7 +5,11 @@ import { NEODASH_VERSION } from "../dashboard/DashboardReducer"; import { loadDashboardFromNeo4jByNameThunk, loadDashboardFromNeo4jByUUIDThunk, loadDashboardThunk, upgradeDashboardVersion } from "../dashboard/DashboardThunks"; import { createNotificationThunk } from "../page/PageThunks"; import { QueryStatus, runCypherQuery } from "../report/ReportQueryRunner"; -import { setPageNumberThunk, updateGlobalParametersThunk, updateGlobalParameterThunk } from "../settings/SettingsThunks"; +import { + setPageNumberThunk, + updateGlobalParametersThunk, + updateSessionParameterThunk +} from "../settings/SettingsThunks"; import { setConnected, setConnectionModalOpen, setConnectionProperties, setDesktopConnectionProperties, resetShareDetails, setShareDetailsFromUrl, setWelcomeScreenOpen, setDashboardToLoadAfterConnecting, @@ -40,7 +44,9 @@ export const createConnectionThunk = (protocol, url, port, database, username, p dispatch(setConnectionProperties(protocol, url, port, database, username, password)); dispatch(setConnectionModalOpen(false)); dispatch(setConnected(true)); - + dispatch(updateSessionParameterThunk("session_uri", protocol + "://" + url + ":" + port)); + dispatch(updateSessionParameterThunk("session_database", database)); + dispatch(updateSessionParameterThunk("session_username", username)); // If we have remembered to load a specific dashboard after connecting to the database, take care of it here. const application = getState().application; if (application.dashboardToLoadAfterConnecting && (application.dashboardToLoadAfterConnecting.startsWith("http") || application.dashboardToLoadAfterConnecting.startsWith("./") || application.dashboardToLoadAfterConnecting.startsWith("/"))) { diff --git a/src/card/Card.tsx b/src/card/Card.tsx index 946ba6fd1..d3cdfb548 100644 --- a/src/card/Card.tsx +++ b/src/card/Card.tsx @@ -19,7 +19,7 @@ import { import {toggleReportSettings} from './CardActions'; import {getReportState} from './CardSelectors'; import {debounce, Dialog, DialogContent} from '@material-ui/core'; -import {getDashboardIsEditable, getDatabase, getGlobalParameters} from '../settings/SettingsSelectors'; +import {getDashboardIsEditable, getDatabase, getGlobalParameters, getSessionParameters} from '../settings/SettingsSelectors'; import {updateGlobalParameterThunk} from '../settings/SettingsThunks'; import {createNotificationThunk} from '../page/PageThunks'; import useDimensions from 'react-cool-dimensions'; @@ -209,7 +209,7 @@ const mapStateToProps = (state, ownProps) => ({ report: getReportState(state, ownProps.index), editable: getDashboardIsEditable(state), database: getDatabase(state, ownProps && ownProps.dashboardSettings ? ownProps.dashboardSettings.pagenumber : undefined, ownProps.index), - globalParameters: getGlobalParameters(state) + globalParameters: {...getGlobalParameters(state), ...getSessionParameters(state)} }); const mapDispatchToProps = dispatch => ({ diff --git a/src/chart/bar/BarChart.tsx b/src/chart/bar/BarChart.tsx index 90c36419c..eb489f6f8 100644 --- a/src/chart/bar/BarChart.tsx +++ b/src/chart/bar/BarChart.tsx @@ -36,28 +36,32 @@ const NeoBarChart = (props: ChartProps) => { const keys = {}; const data: Record[] = records.reduce((data: Record[], row: Record) => { - if (!selection || !selection['index'] || !selection['value']) { - return data; - } - const index = convertRecordObjectToString(row.get(selection['index'])); - const idx = data.findIndex(item => item.index === index) + try { + if (!selection || !selection['index'] || !selection['value']) { + return data; + } + const index = convertRecordObjectToString(row.get(selection['index'])); + const idx = data.findIndex(item => item.index === index) - const key = selection['key'] !== "(none)" ? recordToNative(row.get(selection['key'])) : selection['value']; - const value = recordToNative(row.get(selection['value'])); + const key = selection['key'] !== "(none)" ? recordToNative(row.get(selection['key'])) : selection['value']; + const value = recordToNative(row.get(selection['value'])); - if (isNaN(value)) { - return data; - } - keys[key] = true; + if (isNaN(value)) { + return data; + } + keys[key] = true; - if (idx > -1) { - data[idx][key] = value - } - else { - data.push({ index, [key]: value }) + if (idx > -1) { + data[idx][key] = value + } + else { + data.push({ index, [key]: value }) + } + return data + } catch (e) { + console.error(e); + return []; } - - return data }, []) .map(row => { Object.keys(keys).forEach(key => { @@ -76,7 +80,13 @@ const NeoBarChart = (props: ChartProps) => { const marginBottom = (settings["marginBottom"]) ? settings["marginBottom"] : 40; const legend = (settings["legend"]) ? settings["legend"] : false; const labelRotation = (settings["labelRotation"] != undefined) ? settings["labelRotation"] : 45; - const labelSkipSize = (settings["barValues"]) ? 1 : 2000; + + const labelSkipWidth = (settings["labelSkipWidth"]) ? (settings["labelSkipWidth"]) : 0; + const labelSkipHeight = (settings["labelSkipHeight"]) ? (settings["labelSkipHeight"]) : 0; + const enableLabel = (settings["barValues"]) ? settings["barValues"] : false; + const positionLabel = (settings["positionLabel"]) ? settings["positionLabel"] : 'off'; + + const layout = (settings["layout"]) ? settings["layout"] : 'vertical'; const colorScheme = (settings["colors"]) ? settings["colors"] : 'set2'; const groupMode = (settings["groupMode"]) ? settings["groupMode"] : 'stacked'; const valueScale = (settings["valueScale"]) ? settings["valueScale"] : 'linear'; @@ -103,11 +113,69 @@ const NeoBarChart = (props: ChartProps) => { return } + const BarComponent = ({ bar, borderColor }) => { + let shade = false; + let darkTop = false; + let includeIndex = false; + let x = bar.width/ 2,y = bar.height / 2, textAnchor = "middle"; + if (positionLabel == "top") + if(layout == "vertical") + y = - 10 ; + else + x = bar.width + 10; + else if (positionLabel == "bottom") + if(layout == "vertical") + y = bar.height + 10; + else + x = - 10 ; + + return ( + + {shade ? : <>} + + { darkTop ? : <> } + {includeIndex ? + {bar.data.indexValue} + : <> } + { enableLabel ? + {bar.data.value} + : <> } + + ) + } // TODO: Get rid of duplicate pie slice names... - + + const extraProperties = positionLabel == "off" ? {} : { barComponent : BarComponent }; return { tickPadding: 5, tickRotation: 0, }} - labelSkipWidth={labelSkipSize} - labelSkipHeight={labelSkipSize} + labelSkipWidth={labelSkipWidth} + labelSkipHeight={labelSkipHeight} labelTextColor={{ from: 'color', modifiers: [['darker', 1.6]] }} + { ...extraProperties} legends={(legend) ? [ { dataFrom: 'keys', diff --git a/src/chart/line/LineChart.tsx b/src/chart/line/LineChart.tsx index 3762cc22c..c7878a429 100644 --- a/src/chart/line/LineChart.tsx +++ b/src/chart/line/LineChart.tsx @@ -30,6 +30,8 @@ const NeoLineChart = (props: ChartProps) => { } const [isTimeChart, setIsTimeChart] = React.useState(false); + const [parseFormat, setParseFormat] = React.useState("%Y-%m-%dT%H:%M:%SZ"); + const settings = (props.settings) ? props.settings : {}; const colorScheme = (settings["colors"]) ? settings["colors"] : 'set2'; @@ -88,23 +90,31 @@ const NeoLineChart = (props: ChartProps) => { data: [] })) + const isDate = (x) => { + return x.__isDate__; + } + const isDateTime = (x) => { - return x !== undefined && x.day !== undefined && x.hour !== undefined && x.minute !== undefined && - x.month !== undefined && x.nanosecond !== undefined && x.second !== undefined && x.year !== undefined; + return x.__isDateTime__; + } + + const isDateTimeOrDate = (x) => { + return isDate(x) || isDateTime(x) || x instanceof Date; } records.forEach((row) => { selection['value'].forEach(key => { const index = data.findIndex(item => (item as Record).id === key) - const x: any = row.get(selection['x']) || 0 + let x: any = row.get(selection['x']) || 0 const y: any = recordToNative(row.get(key)) || 0 if (data[index] && !isNaN(y)) { - if (isDateTime(x)) { + if (isDate(x)) { data[index].data.push({ x, y }) - } - if (!isNaN(x)) { + } else if (isDateTime(x)) { + x = new Date(x.toString()); data[index].data.push({ x, y }) } + else data[index].data.push({ x, y }) } }) }) @@ -124,13 +134,20 @@ const NeoLineChart = (props: ChartProps) => { // TODO - Nivo has a bug that, when we switch from a time-axis to a number axis, the visualization breaks. // Therefore, we now require a manual refresh. - const chartIsTimeChart = (data[0] !== undefined && data[0].data[0] !== undefined && data[0].data[0]['x'] !== undefined && isNaN(data[0].data[0]['x'])); + + + const chartIsTimeChart = (data[0] !== undefined && data[0].data[0] !== undefined && data[0].data[0]['x'] !== undefined && isDateTimeOrDate(data[0].data[0]['x'])); + if (isTimeChart !== chartIsTimeChart) { if (!chartIsTimeChart) { return

Line chart switched from time-axis to number-axis. Please re-run the report to see your changes.
; } + + let p = chartIsTimeChart ? (isDateTime(data[0].data[0]['x']) ? "%Y-%m-%dT%H:%M:%SZ": "%Y-%m-%d" ) : ""; + + setParseFormat(p); setIsTimeChart(chartIsTimeChart); } @@ -151,12 +168,13 @@ const NeoLineChart = (props: ChartProps) => { return Invalid tick size specification for time chart. Parameter value must be set to "every [number] ['years', 'months', 'weeks', 'days', 'hours', 'seconds', 'milliseconds']".; } - +//T18:40:32.142+0100 + //%Y-%m-%dT%H:%M:%SZ const lineViz =
{ const keys = {}; const data: Record[] = records.reduce((data: Record[], row: Record) => { - if (!selection || !selection['index'] || !selection['value']) { - return data; - } + try { + if (!selection || !selection['index'] || !selection['value']) { + return data; + } - const index = convertRecordObjectToString(row.get(selection['index'])); - const idx = data.findIndex(item => item.index === index) + const index = convertRecordObjectToString(row.get(selection['index'])); + const idx = data.findIndex(item => item.index === index) + const key = selection['key'] !== "(none)" ? recordToNative(row.get(selection['key'])) : selection['value']; + const value = recordToNative(row.get(selection['value'])); - const key = selection['key'] !== "(none)" ? recordToNative(row.get(selection['key'])) : selection['value']; - const value = recordToNative(row.get(selection['value'])); + if (isNaN(value)) { + return data; + } + keys[key] = true; - if (isNaN(value)) { - return data; - } - keys[key] = true; + if (idx > -1) { + data[idx][key] = value + } + else { + data.push({ id: index, label: index, value: value }) + } - if (idx > -1) { - data[idx][key] = value - } - else { - data.push({ id: index, label: index, value: value }) + return data + } catch (e) { + console.error(e); + return []; } - - return data }, []) .map(row => { Object.keys(keys).forEach(key => { @@ -99,7 +103,7 @@ const NeoPieChart = (props: ChartProps) => { if (data.length == 0) { return } - + return (dispatch: any, getState: any) => { dispatch(resetDashboardState()); return } - const dashboard = JSON.parse(text); + var dashboard = JSON.parse(text); + + // If we load a debug report, take out the 'dashboard' value and set it to safe values. + if (dashboard["_persist"] && dashboard["application"] && dashboard["dashboard"]) { + dispatch(createNotificationThunk("Loaded a Debug Report", "Recovery-mode active. All report types were set to 'table'.")); + dashboard['dashboard']['pages'].map(p => { + p['reports'].map(r => { + r['type'] = 'table'; + }) + }); + dashboard = dashboard['dashboard']; + } + // Attempt upgrade if dashboard version is outdated. if (dashboard["version"] == "1.1") { diff --git a/src/modal/AboutModal.tsx b/src/modal/AboutModal.tsx index fd9d6fea7..f655c595d 100644 --- a/src/modal/AboutModal.tsx +++ b/src/modal/AboutModal.tsx @@ -12,7 +12,7 @@ import BugReportIcon from '@material-ui/icons/BugReport'; export const NeoAboutModal = ({ open, handleClose, getDebugState }) => { const app = "NeoDash - Neo4j Dashboard Builder"; - const version = "2.1.9"; + const version = "2.1.10"; const downloadDebugFile = () => { const element = document.createElement("a"); diff --git a/src/modal/ShareModal.tsx b/src/modal/ShareModal.tsx index 04b4cfb53..d4a06a266 100644 --- a/src/modal/ShareModal.tsx +++ b/src/modal/ShareModal.tsx @@ -23,6 +23,7 @@ import { applicationGetConnection } from '../application/ApplicationSelectors'; // const shareBaseURL = "http://localhost:3000"; const shareBaseURL = "http://neodash.graphapp.io"; +const shareLocalURL = window.location.origin.startsWith("file")? shareBaseURL : window.location.origin; const styles = { }; @@ -42,6 +43,8 @@ export const NeoShareModal = ({ connection, loadDashboardListFromNeo4j, loadData const [shareFileURL, setShareFileURL] = React.useState(""); const [shareConnectionDetails, setShareConnectionDetails] = React.useState("No"); const [shareStandalone, setShareStandalone] = React.useState("No"); + const [selfHosted, setSelfHosted] = React.useState("No"); + const [shareLink, setShareLink] = React.useState(null); @@ -164,6 +167,7 @@ export const NeoShareModal = ({ connection, loadDashboardListFromNeo4j, loadData setShareConnectionDetails(e) }} /> + {shareLocalURL != shareBaseURL ? : <>} + { + setShareLink(null); + setSelfHosted(e); + }} />