Skip to content

Commit 24e47c5

Browse files
committed
Finalized save/load to Neo4j feature
1 parent 3deac29 commit 24e47c5

File tree

4 files changed

+67
-47
lines changed

4 files changed

+67
-47
lines changed

src/dashboard/DashboardHeader.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const styles = {
3232

3333

3434
export const NeoDashboardHeader = ({ classes, open, pagenumber, pages, dashboardTitle,
35-
handleDrawerOpen, setDashboardTitle, editable, connection,
35+
handleDrawerOpen, setDashboardTitle, editable, connection,
3636
addPage, removePage, selectPage, setPageTitle, onConnectionModalOpen }) => {
3737

3838
const [dashboardTitleText, setDashboardTitleText] = React.useState(dashboardTitle);
@@ -96,8 +96,10 @@ export const NeoDashboardHeader = ({ classes, open, pagenumber, pages, dashboard
9696
maxRows={4}
9797
value={dashboardTitleText}
9898
onChange={(event) => {
99-
setDashboardTitleText(event.target.value);
100-
debouncedDashboardTitleUpdate(event.target.value);
99+
if (editable) {
100+
setDashboardTitleText(event.target.value);
101+
debouncedDashboardTitleUpdate(event.target.value);
102+
}
101103
}}
102104
/>
103105
<Tooltip title={connection.protocol + "://" + connection.url + ":" + connection.port} placement="left" aria-label="host">
@@ -115,7 +117,7 @@ export const NeoDashboardHeader = ({ classes, open, pagenumber, pages, dashboard
115117
boxShadow: "2px 1px 10px 0px rgb(0 0 0 / 12%)",
116118
borderBottom: "1px solid lightgrey"
117119
}}>
118-
{pages.map((page, i) =>
120+
{pages.map((page, i) =>
119121
<NeoPageButton index={i} title={page.title} selected={pagenumber == i}
120122
disabled={!editable}
121123
onSelect={() => selectPage(i)}

src/dashboard/DashboardThunks.tsx

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,17 @@ export const addPageThunk = () => (dispatch: any, getState: any) => {
4444

4545
export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => {
4646
try {
47-
if(text.length == 0){
48-
throw("No dashboard file specified. Did you select a file?")
47+
if (text.length == 0) {
48+
throw ("No dashboard file specified. Did you select a file?")
4949
}
50-
if(text.trim() == "{}"){
50+
if (text.trim() == "{}") {
5151
dispatch(resetDashboardState());
52-
return
52+
return
5353
}
5454
const dashboard = JSON.parse(text);
55-
55+
5656
// Attempt upgrade
57-
if(dashboard["version"] == "1.1"){
57+
if (dashboard["version"] == "1.1") {
5858
const upgradedDashboard = {};
5959
upgradedDashboard["title"] = dashboard["title"];
6060
upgradedDashboard["version"] = "2.0";
@@ -69,10 +69,10 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => {
6969
const newPageReports = [];
7070
p["reports"].forEach(r => {
7171
// only migrate value report types.
72-
if(["table", "graph", "bar", "line", "map", "value", "json", "select", "iframe", "text"].indexOf(r["type"]) == -1){
72+
if (["table", "graph", "bar", "line", "map", "value", "json", "select", "iframe", "text"].indexOf(r["type"]) == -1) {
7373
return
7474
}
75-
if(r["type"] == "select"){
75+
if (r["type"] == "select") {
7676
r["query"] = "";
7777
}
7878
const newPageReport = {
@@ -83,9 +83,9 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => {
8383
parameters: r["parameters"],
8484
query: r["query"],
8585
selection: {},
86-
settings: {}
86+
settings: {}
8787
}
88-
88+
8989
newPageReports.push(newPageReport);
9090
})
9191
newPage["reports"] = newPageReports;
@@ -95,38 +95,60 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => {
9595

9696
dispatch(setDashboard(upgradedDashboard))
9797
dispatch(createNotificationThunk("Successfully upgraded dashboard", "Your old dashboard was migrated to version 2.0. You might need to refresh this page."));
98-
return
98+
return
9999
}
100-
if(dashboard["version"] != "2.0"){
101-
throw ("Invalid dashboard version: "+dashboard.version);
100+
if (dashboard["version"] != "2.0") {
101+
throw ("Invalid dashboard version: " + dashboard.version);
102102
}
103-
103+
104104
dispatch(setDashboard(dashboard))
105105

106106
} catch (e) {
107107
dispatch(createNotificationThunk("Unable to load dashboard", e));
108108
}
109109
}
110110

111-
export const saveDashboardToNeo4jThunk = (dashboard, date, user) => (dispatch: any, getState: any) => {
111+
export const saveDashboardToNeo4jThunk = (driver, dashboard, date, user) => (dispatch: any, getState: any) => {
112112
try {
113113
const uuid = createUUID();
114114
const title = dashboard.title;
115115
// const user = user;
116116
// const date = date;
117117
const version = dashboard.version;
118118
const content = dashboard;
119-
console.log(uuid);
120-
dispatch(createNotificationThunk("Unable to save dashboard to Neo4j", ""));
119+
runCypherQuery(driver, getState().application.connection.database,
120+
"CREATE (n:_Neodash_Dashboard) SET n.uuid = $uuid, n.title = $title, n.version = $version, n.user = $user, n.content = $content, n.date = datetime($date) RETURN $uuid as uuid",
121+
{ uuid: uuid, title: title, version: version, user: user, content: JSON.stringify(dashboard, null, 2), date: date },
122+
{}, ["uuid"], 1, () => { return }, (records) => {
123+
console.log(records);
124+
dispatch(createNotificationThunk("🎉 Success!", "Your current dashboard was saved to Neo4j."))
125+
});
126+
121127
} catch (e) {
122128
dispatch(createNotificationThunk("Unable to save dashboard to Neo4j", e));
123129
}
124130
}
125131

126132
export const loadDashboardFromNeo4jThunk = (driver, uuid, callback) => (dispatch: any, getState: any) => {
127133
try {
128-
runCypherQuery(driver, getState().application.connection.database, "RETURN $uuid as dashboard", {uuid: uuid}, {}, ["dashboard"], 1, () => { return }, (records) => callback(records[0]['_fields'][0]))
134+
runCypherQuery(driver, getState().application.connection.database, "MATCH (n:_Neodash_Dashboard) WHERE n.uuid = $uuid RETURN n.content as dashboard", { uuid: uuid }, {}, ["dashboard"], 1, () => { return }, (records) => callback(records[0]['_fields'][0]))
129135
} catch (e) {
130136
dispatch(createNotificationThunk("Unable to load dashboard to Neo4j", e));
131137
}
132-
}
138+
}
139+
140+
export const loadDashboardListFromNeo4jThunk = (driver, callback) => (dispatch: any, getState: any) => {
141+
try {
142+
runCypherQuery(driver, getState().application.connection.database,
143+
"MATCH (n:_Neodash_Dashboard) RETURN n.uuid as id, n.title as title, toString(n.date) as date, n.user as author ORDER BY date DESC",
144+
{}, {}, ["id, title, date, user"], 1000, () => { return }, (records) => {
145+
const result = records.map(r => {
146+
return { id: r["_fields"][0], title: r["_fields"][1], date: r["_fields"][2], author: r["_fields"][3] };
147+
});
148+
callback(result);
149+
})
150+
} catch (e) {
151+
dispatch(createNotificationThunk("Unable to load dashboard list from Neo4j", e));
152+
}
153+
}
154+

src/modal/LoadModal.tsx

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { connect } from "react-redux";
1414
import SystemUpdateAltIcon from '@material-ui/icons/SystemUpdateAlt';
1515
import PostAddIcon from '@material-ui/icons/PostAdd';
1616
import StorageIcon from '@material-ui/icons/Storage';
17-
import { loadDashboardFromNeo4jThunk, loadDashboardThunk } from '../dashboard/DashboardThunks';
17+
import { loadDashboardFromNeo4jThunk, loadDashboardListFromNeo4jThunk, loadDashboardThunk } from '../dashboard/DashboardThunks';
1818
import { DataGrid } from '@mui/x-data-grid';
1919
import { Neo4jContext, Neo4jContextState } from "use-neo4j/dist/neo4j.context";
2020

@@ -27,7 +27,7 @@ const styles = {
2727

2828
};
2929

30-
export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, connection }) => {
30+
export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, loadDashboardListFromNeo4j }) => {
3131
const [loadModalOpen, setLoadModalOpen] = React.useState(false);
3232
const [loadFromNeo4jModalOpen, setLoadFromNeo4jModalOpen] = React.useState(false);
3333
const [text, setText] = React.useState("");
@@ -49,15 +49,11 @@ export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, connection
4949
setText("");
5050
};
5151

52-
function handleDashboardLoadedFromNeo4j(result){
52+
function handleDashboardLoadedFromNeo4j(result) {
5353
setText(result);
5454
setLoadFromNeo4jModalOpen(false);
5555
}
5656

57-
function handleGetDashboardListLoadedFromNeo4j(result){
58-
setRows(result);
59-
}
60-
6157
const reader = new FileReader();
6258
reader.onload = async (e) => {
6359
setText(e.target.result);
@@ -80,14 +76,6 @@ export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, connection
8076
},
8177
]
8278

83-
// const rows = [
84-
// { id: '6c7a822c-243b-4ed5-ac2d-1ab9d84ae8e0', title: 'My Covid Dashboard Name', date: 'Nov 27 2021 21:08:09', author: 'neo4j', load: <b>Load</b> },
85-
// { id: '6c7a822c-243b-4ed5-ac2d-1ab9d84ae8e1', title: 'Mark', date: 'Nov 27 2021 21:07:09', author: 'neo4j', load: <b>Load</b> },
86-
// { id: '6c7a822c-243b-4ed5-ac2d-1ab9d84ae8e2', title: 'Mark', date: 'Nov 27 2021 21:07:09', author: 'neo4j', load: <b>Load</b> },
87-
// { id: '6c7a822c-243b-4ed5-ac2d-1ab9d84ae8e3', title: 'Mark', date: 'Nov 27 2021 21:07:09', author: 'neo4j', load: <b>Load</b> },
88-
// { id: '6c7a822c-243b-4ed5-ac2d-1ab9d84ae8e4', title: 'Mark', date: 'Nov 27 2021 21:07:09', author: 'neo4j', load: <b>Load</b> },
89-
// { id: '6c7a822c-243b-4ed5-ac2d-1ab9d84ae8e5', title: 'Mark', date: 'Nov 27 2021 21:07:09', author: 'neo4j', load: <b>Load</b> },
90-
// ];
9179

9280
return (
9381
<div>
@@ -136,7 +124,10 @@ export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, connection
136124
</Button>
137125
<Button
138126
component="label"
139-
onClick={(e) => setLoadFromNeo4jModalOpen(true)}
127+
onClick={(e) => {
128+
loadDashboardListFromNeo4j(driver, (result) => {setRows(result)});
129+
setLoadFromNeo4jModalOpen(true);
130+
}}
140131
style={{ marginLeft: "10px", marginBottom: "10px", backgroundColor: "white" }}
141132
color="default"
142133
variant="contained"
@@ -170,7 +161,7 @@ export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, connection
170161
</Dialog>
171162
<Dialog maxWidth={"lg"} open={loadFromNeo4jModalOpen} onClose={(e) => { setLoadFromNeo4jModalOpen(false) }} aria-labelledby="form-dialog-title">
172163
<DialogTitle id="form-dialog-title">
173-
Load From Neo4j
164+
Select From Neo4j
174165
<IconButton onClick={(e) => { setLoadFromNeo4jModalOpen(false) }} style={{ padding: "3px", float: "right" }}>
175166
<Badge badgeContent={""} >
176167
<CloseIcon />

src/modal/SaveModal.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import React from 'react';
1+
import React, { useContext } from 'react';
22
import Button from '@material-ui/core/Button';
3-
import TextField from '@material-ui/core/TextField';
43
import Dialog from '@material-ui/core/Dialog';
54
import DialogActions from '@material-ui/core/DialogActions';
65
import DialogContent from '@material-ui/core/DialogContent';
@@ -21,6 +20,7 @@ import { valueIsArray, valueIsObject } from '../report/RecordProcessing';
2120
import StorageIcon from '@material-ui/icons/Storage';
2221
import { applicationGetConnection } from '../application/ApplicationSelectors';
2322
import { saveDashboardToNeo4jThunk } from '../dashboard/DashboardThunks';
23+
import { Neo4jContext, Neo4jContextState } from "use-neo4j/dist/neo4j.context";
2424

2525
/**
2626
* A modal to save a dashboard as a JSON text string.
@@ -63,6 +63,7 @@ const filterNestedDict = (value: any, removedKeys: any[]) => {
6363
export const NeoSaveModal = ({ dashboard, connection, saveDashboardToNeo4j}) => {
6464
const [saveModalOpen, setSaveModalOpen] = React.useState(false);
6565
const [saveToNeo4jModalOpen, setSaveToNeo4jModalOpen] = React.useState(false);
66+
const { driver } = useContext<Neo4jContextState>(Neo4jContext);
6667

6768
const handleClickOpen = () => {
6869
setSaveModalOpen(true);
@@ -165,14 +166,18 @@ export const NeoSaveModal = ({ dashboard, connection, saveDashboardToNeo4j}) =>
165166
style={{ width: "100%", border: "1px solid lightgray" }}
166167
className={"textinput-linenumbers"}
167168
value={"{\n title: '" + dashboard.title + "',\n" +
168-
" date: '" + new Date().toString() + "',\n" +
169+
" date: '" + new Date().toISOString() + "',\n" +
169170
" user: '" + connection.username + "',\n" +
170171
" content: " + "{...}"+ "\n}" }
171172
aria-label=""
172173
placeholder="" />
173174
<Button
174175
component="label"
175-
onClick={e => {saveDashboardToNeo4j(dashboard, new Date().toString(), connection.username)}}
176+
onClick={e => {
177+
saveDashboardToNeo4j(driver, dashboard, new Date().toISOString(), connection.username);
178+
setSaveToNeo4jModalOpen(false);
179+
setSaveModalOpen(false);
180+
}}
176181
style={{ backgroundColor: "white",marginTop: "20px", float: "right" }}
177182
color="default"
178183
variant="contained"
@@ -183,7 +188,7 @@ export const NeoSaveModal = ({ dashboard, connection, saveDashboardToNeo4j}) =>
183188
<Button
184189
component="label"
185190
onClick={(e) => { setSaveToNeo4jModalOpen(false) }}
186-
style={{ float: "right", marginTop: "20px", marginRight: "10px", ackgroundColor: "white" }}
191+
style={{ float: "right", marginTop: "20px", marginRight: "10px", backgroundColor: "white" }}
187192
color="default"
188193
variant="contained"
189194
size="medium">
@@ -204,8 +209,8 @@ const mapStateToProps = state => ({
204209
});
205210

206211
const mapDispatchToProps = dispatch => ({
207-
saveDashboardToNeo4j: (dashboard: any, date: any, user: any) => {
208-
dispatch(saveDashboardToNeo4jThunk(dashboard, date, user))
212+
saveDashboardToNeo4j: (driver: any, dashboard: any, date: any, user: any) => {
213+
dispatch(saveDashboardToNeo4jThunk(driver, dashboard, date, user))
209214
},
210215
});
211216

0 commit comments

Comments
 (0)