@@ -2,89 +2,108 @@ import React from "react";
22import Section from "react-materialize/lib/Section" ;
33import Row from "react-materialize/lib/Row" ;
44import Container from "react-materialize/lib/Container" ;
5- import { AddNeoCard , NeoCard } from "./card/NeoCard" ;
5+ import { AddNeoCard , NeoCardComponent } from "./card/NeoCard" ;
66import Navbar from "react-materialize/lib/Navbar" ;
77import Icon from "react-materialize/lib/Icon" ;
88import NeoModal from "./component/NeoModal" ;
99import Textarea from "react-materialize/lib/Textarea" ;
1010import NavItem from "react-materialize/lib/NavItem" ;
1111import Button from "react-materialize/lib/Button" ;
12- import Col from "react-materialize/lib/Col" ;
1312import NeoTextInput from "./component/NeoTextInput" ;
1413import neo4j from "neo4j-driver" ;
15- import { Checkbox } from "react-materialize" ;
1614import NeoCheckBox from "./component/NeoCheckBox" ;
1715import NeoTextButton from "./component/NeoTextButton" ;
18- import defaultDashboard from './default_dashboard.json' ;
19-
16+ import defaultDashboard from './data/default_dashboard.json' ;
17+ import DesktopIntegration from './tools/DesktopIntegration' ;
18+
19+
20+ /**
21+ * Main class for the NeoDash dashboard builder component.
22+ *
23+ * This component handles:
24+ * - Connecting to Neo4j (through a Neo4j Desktop integration or manually)
25+ * - Loading/storing dashboards as JSON (optionally from the browser cache)
26+ * - The creation, ordering and deleting of the NeoCard components.
27+ * - Propagating global parameter changes ("Selection" reports) to each of the cards.
28+ */
2029class NeoDash extends React . Component {
2130 version = '1.0' ;
2231
2332 constructor ( props ) {
2433 super ( props ) ;
34+ this . stateChanged = this . stateChanged . bind ( this ) ;
35+ this . loadCardsFromJSON = this . loadCardsFromJSON . bind ( this ) ;
36+ this . connect = this . connect . bind ( this ) ;
2537
26- // Neo4j Desktop integration
27- let neo4jDesktopApi = window . neo4jDesktopApi ;
28- if ( neo4jDesktopApi ) {
29- let promise = neo4jDesktopApi . getContext ( ) ;
30- let a = this ;
31- promise . then ( function ( context ) {
32-
33- let desktopIntegration = new Neo4jDesktopIntegration ( context ) ;
34- let neo4j = desktopIntegration . getActiveDatabase ( ) ;
35- if ( neo4j ) {
36- a . connection = {
37- url : neo4j . connection . configuration . protocols . bolt . url ,
38- database : "" ,
39- username : neo4j . connection . configuration . protocols . bolt . username ,
40- password : neo4j . connection . configuration . protocols . bolt . password ,
41- encryption : neo4j . connection . configuration . protocols . bolt . tlsLevel === "REQUIRED" ? "on" : "off"
42- }
43- a . connect ( )
38+ // Attempt to load an existing dashboard from the browser cache.
39+ this . loadDashboardfromBrowserCache ( ) ;
4440
45- } else {
46- a . updateConnectionModal ( a . connect , true ) ;
47- a . stateChanged ( { label : "HideError" } )
48- }
49- } ) ;
41+ if ( window . neo4jDesktopApi ) {
42+ this . setConnectionDetailsFromDesktopIntegration ( ) ;
43+ } else {
44+ // check the browser cache or use default values.
45+ this . setConnectionDetailsFromBrowserCache ( ) ;
46+ }
47+
48+ }
5049
50+ /**
51+ * Sets the state of NeoDash based on
52+ */
53+ loadDashboardfromBrowserCache ( ) {
54+ this . state = { json : '{}' , count : 0 }
55+ if ( localStorage . getItem ( 'neodash-dashboard' ) ) {
56+ this . state . json = localStorage . getItem ( 'neodash-dashboard' ) ;
5157 }
58+ }
5259
53- // check the browser cache or use default values.
60+ /**
61+ * Sets the connection details based on the data in the browser cache (if available).
62+ * Else, defaults to localhost:7867.
63+ */
64+ setConnectionDetailsFromBrowserCache ( ) {
5465 this . connection = {
5566 url : ( localStorage . getItem ( 'neodash-url' ) ) ? localStorage . getItem ( 'neodash-url' ) : 'neo4j://localhost:7687' ,
5667 database : ( localStorage . getItem ( 'neodash-database' ) ) ? localStorage . getItem ( 'neodash-database' ) : '' ,
5768 username : ( localStorage . getItem ( 'neodash-username' ) ) ? localStorage . getItem ( 'neodash-username' ) : 'neo4j' ,
5869 password : ( localStorage . getItem ( 'neodash-password' ) ) ? localStorage . getItem ( 'neodash-password' ) : '' ,
5970 encryption : ( localStorage . getItem ( 'neodash-encryption' ) ) ? localStorage . getItem ( 'neodash-encryption' ) : 'off' ,
6071 }
72+ this . updateConnectionModal ( this . connect , true ) ;
73+ this . stateChanged ( { label : "HideError" } )
74+ }
6175
62- this . state = { json : '{}' , count : 0 }
63- if ( localStorage . getItem ( 'neodash-dashboard' ) ) {
64- this . state . json = localStorage . getItem ( 'neodash-dashboard' ) ;
65- }
66-
67- this . stateChanged = this . stateChanged . bind ( this ) ;
68- this . loadJson = this . loadJson . bind ( this ) ;
69- this . connect = this . connect . bind ( this ) ;
70-
71- // If not running from desktop, always ask for connection details
72- if ( ! neo4jDesktopApi ) {
73- this . updateConnectionModal ( this . connect , true ) ;
74- } else {
75- // If running from desktop, the constructor will set up a connection using the promise.
76- this . stateChanged ( { label : "CreateError" , value : "Trying to connect to your active database..." } ) ;
77- }
78-
79-
76+ /**
77+ * Attempts to set the connection details using the Neo4j Desktop integration.
78+ * If NeoDash is running from Desktop, find the first active database and connect to it.
79+ * If no databases are active, default to a manually specified connection (possibly from browser cache)
80+ */
81+ setConnectionDetailsFromDesktopIntegration ( ) {
82+ let promise = window . neo4jDesktopApi . getContext ( ) ;
83+ let neodash = this ;
84+ promise . then ( function ( context ) {
85+ let neo4jDesktopIntegration = new DesktopIntegration ( context ) ;
86+ let connection = neo4jDesktopIntegration . connection ;
87+ if ( connection ) {
88+ neodash . connection = connection ;
89+ neodash . stateChanged ( { label : "CreateError" , value : "Trying to connect to your active database..." } ) ;
90+ neodash . connect ( ) ;
91+ } else {
92+ neodash . setConnectionDetailsFromBrowserCache ( ) ;
93+ }
94+ } ) ;
8095 }
8196
8297 componentDidMount ( ) {
83- this . loadJson ( )
98+ this . loadCardsFromJSON ( )
8499 }
85100
86101
87- connect ( e ) {
102+ /**
103+ * Will try to connect to Neo4j given the specified connection parameters.
104+ * On failure, will produce an error message and show it to the user.
105+ */
106+ connect ( ) {
88107 try {
89108 var url = this . connection . url ;
90109 if ( ! ( url . startsWith ( "bolt://" ) || url . startsWith ( "bolt+routing://" ) || url . startsWith ( "neo4j://" ) ) ) {
@@ -104,11 +123,8 @@ class NeoDash extends React.Component {
104123 . then ( result => {
105124 this . errorModal = null ;
106125 this . connected = true ;
107-
108-
109126 this . updateConnectionModal ( this . connect , false ) ;
110- this . loadJson ( )
111-
127+ this . loadCardsFromJSON ( )
112128 localStorage . setItem ( 'neodash-database' , this . connection . database ) ;
113129 localStorage . setItem ( 'neodash-url' , this . connection . url ) ;
114130 localStorage . setItem ( 'neodash-username' , this . connection . username ) ;
@@ -132,7 +148,7 @@ class NeoDash extends React.Component {
132148 }
133149
134150
135- loadJson ( ) {
151+ loadCardsFromJSON ( ) {
136152 if ( ! this . connected ) {
137153 this . state . title = 'NeoDash ⚡' ;
138154 return
@@ -158,22 +174,28 @@ class NeoDash extends React.Component {
158174 this . state . cardState = loaded . reports . map ( c => [ ] ) ;
159175 this . state . cards = loaded . reports . map ( ( report , index ) => {
160176 if ( report . type ) {
161- return < NeoCard
177+ return < NeoCardComponent
162178 connection = { this . connection }
163179 globalParameters = { this . state . globalParameters }
164- page = { report . page } width = { report . width } height = { report . height }
165- kkey = { this . state . count + index } key = { this . state . count + index } id = { index }
180+ page = { report . page }
181+ width = { report . width }
182+ height = { report . height }
183+ kkey = { this . state . count + index }
184+ key = { this . state . count + index }
185+ id = { index }
166186 session = { this . session }
167187 onChange = { this . stateChanged }
168188 editable = { this . state . editable }
169- type = { report . type } propertiesSelected = { report . properties }
189+ type = { report . type }
190+ propertiesSelected = { report . properties }
170191 title = { report . title }
171- query = { report . query } parameters = { report . parameters }
192+ query = { report . query }
193+ parameters = { report . parameters }
172194 refresh = { report . refresh } />
173195 } else if ( this . state . editable ) {
174196 return < AddNeoCard key = { 99999999 } id = { 99999999 } onClick = { this . stateChanged } />
175197 }
176- return < div > </ div >
198+ return < div / >
177199 }
178200 ) ;
179201 this . state . count = this . state . count + ( ( loaded . reports . length ) ? loaded . reports . length : 0 ) - 1 ;
@@ -194,9 +216,8 @@ class NeoDash extends React.Component {
194216
195217
196218 setDefaultDashboard ( ) {
197- let state = this . state ;
198219 this . state . json = JSON . stringify ( defaultDashboard ) ;
199- this . loadJson ( )
220+ this . loadCardsFromJSON ( )
200221 }
201222
202223 stateChanged ( update ) {
@@ -272,21 +293,21 @@ class NeoDash extends React.Component {
272293 this . state . cardState . splice ( index , 1 ) ;
273294 }
274295 if ( update . label !== "SaveModalUpdated" ) {
275- this . handleUpdateSavedJSON ( ) ;
296+ this . buildJSONFromReportsState ( ) ;
276297 }
277298 this . setState ( this . state ) ;
278299 }
279300
280301 handleNewCardCreate ( ) {
281- let newCard = < NeoCard connection = { this . connection }
282- globalParameters = { this . state . globalParameters }
283- kkey = { this . state . count }
284- session = { this . session }
285- width = { 4 } height = { 4 }
286- id = { this . state . count }
287- editable = { this . state . editable }
288- key = { this . state . count }
289- onChange = { this . stateChanged } type = 'table' /> ;
302+ let newCard = < NeoCardComponent connection = { this . connection }
303+ globalParameters = { this . state . globalParameters }
304+ kkey = { this . state . count }
305+ session = { this . session }
306+ width = { 4 } height = { 4 }
307+ id = { this . state . count }
308+ editable = { this . state . editable }
309+ key = { this . state . count }
310+ onChange = { this . stateChanged } type = 'table' /> ;
290311 this . state . count += 1 ;
291312 this . state . cards . splice ( this . state . cards . length - 1 , 0 , newCard ) ;
292313 this . state . cardState . splice ( this . state . cardState . length - 1 , 0 , {
@@ -334,7 +355,7 @@ class NeoDash extends React.Component {
334355 * Helper function to convert a string with capital letters and spaces to a lowercase snake case verison.
335356 */
336357 toLowerCaseSnakeCase ( value ) {
337- return value . toLowerCase ( ) . replace ( / / g, "_" ) ;
358+ return value . toLowerCase ( ) . replace ( / / g, "_" ) ;
338359 }
339360
340361 handleError ( content ) {
@@ -364,7 +385,7 @@ class NeoDash extends React.Component {
364385 ] } />
365386 }
366387
367- handleUpdateSavedJSON ( ) {
388+ buildJSONFromReportsState ( ) {
368389 if ( ! this . connected ) {
369390 return
370391 }
@@ -441,8 +462,8 @@ class NeoDash extends React.Component {
441462 < NeoModal
442463 header = { 'Connect to Neo4j' }
443464 style = { { 'maxWidth' : '520px' } }
444- key = { this . state . count }
445- id = { this . state . count }
465+ key = { ( this . state ) ? this . state . count : 0 }
466+ id = { ( this . state ) ? this . state . count : 0 }
446467 footerType = { "modal-dark-footer" }
447468 open = { open }
448469 root = { document . getElementById ( "root" ) }
@@ -507,7 +528,8 @@ class NeoDash extends React.Component {
507528 }
508529
509530 render ( ) {
510- this . generateSaveLoadModal ( this . loadJson ) ;
531+ // This should not be called in render...
532+ this . generateSaveLoadModal ( this . loadCardsFromJSON ) ;
511533 let title = < Textarea disabled = { ! this . state . editable } noLayout = { true }
512534 style = { { "width" : '500px' } }
513535 className = "card-title editable-title"
@@ -540,24 +562,5 @@ class NeoDash extends React.Component {
540562 }
541563}
542564
543- class Neo4jDesktopIntegration {
544- constructor ( context ) {
545- this . desktopContext = context ;
546- }
547-
548- getActiveDatabase ( ) {
549-
550- for ( let pi = 0 ; pi < this . desktopContext . projects . length ; pi ++ ) {
551- let prj = this . desktopContext . projects [ pi ] ;
552- for ( let gi = 0 ; gi < prj . graphs . length ; gi ++ ) {
553- let grf = prj . graphs [ gi ] ;
554- if ( grf . status == 'ACTIVE' ) {
555- return grf ;
556- }
557- }
558- }
559- return null ;
560- }
561- }
562565
563566export default ( NeoDash ) ;
0 commit comments