Skip to content

Commit 3deac29

Browse files
committed
Adding placeholders for load/save to database feature. Tweaked naming
1 parent 1183ded commit 3deac29

File tree

5 files changed

+184
-31
lines changed

5 files changed

+184
-31
lines changed

src/card/settings/custom/CardSettingsContentPropertySelect.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const NeoCardSettingsContentPropertySelect = ({ type, database, query, onQueryUp
8383
id="autocomplete-property"
8484
options={propertyRecords.map(r => r["_fields"] ? r["_fields"][0] : "(no data)")}
8585
getOptionLabel={(option) => option}
86-
style={{ display: "inline-block", width: 250, marginLeft: "5px", marginTop: "5px" }}
86+
style={{ display: "inline-block", width: 200, marginLeft: "5px", marginTop: "5px" }}
8787
inputValue={propertyInputText}
8888
onInputChange={(event, value) => {
8989
setPropertyInputText(value);
@@ -104,9 +104,9 @@ const NeoCardSettingsContentPropertySelect = ({ type, database, query, onQueryUp
104104
}}
105105
renderInput={(params) => <TextField {...params} placeholder="Start typing..." InputLabelProps={{ shrink: true }} label={"Property Name"} />}
106106
/>
107-
<NeoFieldSelection placeholder='id'
108-
label="ID (optional)" numeric={true} value={idValue}
109-
style={{ width: "90px", marginTop: "5px", marginLeft: "10px" }}
107+
<NeoFieldSelection placeholder='number'
108+
label="Number (optional)" numeric={true} value={idValue}
109+
style={{ width: "140px", marginTop: "5px", marginLeft: "10px" }}
110110
onChange={(value) => {
111111
const newValue = value ? "_" + value : "";
112112
setIdValue(value);

src/config/ReportConfig.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export const REPORT_TYPES = {
265265
label: "Show Legend",
266266
type: SELECTION_TYPES.LIST,
267267
values: [true, false],
268-
default: true
268+
default: false
269269
},
270270
"colors": {
271271
label: "Color Scheme",

src/dashboard/DashboardThunks.tsx

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import { createNotificationThunk } from "../page/PageThunks";
2-
import { setPageNumber, updateDashboardSetting } from "../settings/SettingsActions";
2+
import { updateDashboardSetting } from "../settings/SettingsActions";
33
import { addPage, removePage, resetDashboardState, setDashboard } from "./DashboardActions";
4+
import { runCypherQuery } from "../report/CypherQueryRunner";
5+
6+
7+
function createUUID() {
8+
var dt = new Date().getTime();
9+
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
10+
var r = (dt + Math.random() * 16) % 16 | 0;
11+
dt = Math.floor(dt / 16);
12+
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
13+
});
14+
return uuid;
15+
}
416

517

618
export const removePageThunk = (number) => (dispatch: any, getState: any) => {
@@ -94,4 +106,27 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => {
94106
} catch (e) {
95107
dispatch(createNotificationThunk("Unable to load dashboard", e));
96108
}
109+
}
110+
111+
export const saveDashboardToNeo4jThunk = (dashboard, date, user) => (dispatch: any, getState: any) => {
112+
try {
113+
const uuid = createUUID();
114+
const title = dashboard.title;
115+
// const user = user;
116+
// const date = date;
117+
const version = dashboard.version;
118+
const content = dashboard;
119+
console.log(uuid);
120+
dispatch(createNotificationThunk("Unable to save dashboard to Neo4j", ""));
121+
} catch (e) {
122+
dispatch(createNotificationThunk("Unable to save dashboard to Neo4j", e));
123+
}
124+
}
125+
126+
export const loadDashboardFromNeo4jThunk = (driver, uuid, callback) => (dispatch: any, getState: any) => {
127+
try {
128+
runCypherQuery(driver, getState().application.connection.database, "RETURN $uuid as dashboard", {uuid: uuid}, {}, ["dashboard"], 1, () => { return }, (records) => callback(records[0]['_fields'][0]))
129+
} catch (e) {
130+
dispatch(createNotificationThunk("Unable to load dashboard to Neo4j", e));
131+
}
97132
}

src/modal/LoadModal.tsx

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useContext } from 'react';
22
import Button from '@material-ui/core/Button';
33
import Dialog from '@material-ui/core/Dialog';
44
import DialogContent from '@material-ui/core/DialogContent';
@@ -14,7 +14,10 @@ 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 { loadDashboardThunk } from '../dashboard/DashboardThunks';
17+
import { loadDashboardFromNeo4jThunk, loadDashboardThunk } from '../dashboard/DashboardThunks';
18+
import { DataGrid } from '@mui/x-data-grid';
19+
import { Neo4jContext, Neo4jContextState } from "use-neo4j/dist/neo4j.context";
20+
1821
/**
1922
* A modal to save a dashboard as a JSON text string.
2023
* The button to open the modal is intended to use in a drawer at the side of the page.
@@ -24,25 +27,37 @@ const styles = {
2427

2528
};
2629

27-
export const NeoLoadModal = ({ loadDashboard }) => {
28-
const [open, setOpen] = React.useState(false);
30+
export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, connection }) => {
31+
const [loadModalOpen, setLoadModalOpen] = React.useState(false);
32+
const [loadFromNeo4jModalOpen, setLoadFromNeo4jModalOpen] = React.useState(false);
2933
const [text, setText] = React.useState("");
34+
const [rows, setRows] = React.useState([]);
35+
const { driver } = useContext<Neo4jContextState>(Neo4jContext);
3036

3137
const handleClickOpen = () => {
32-
setOpen(true);
38+
setLoadModalOpen(true);
3339
};
3440

3541
const handleClose = () => {
36-
setOpen(false);
42+
setLoadModalOpen(false);
3743
};
3844

3945

4046
const handleCloseAndLoad = () => {
41-
setOpen(false);
47+
setLoadModalOpen(false);
4248
loadDashboard(text);
4349
setText("");
4450
};
4551

52+
function handleDashboardLoadedFromNeo4j(result){
53+
setText(result);
54+
setLoadFromNeo4jModalOpen(false);
55+
}
56+
57+
function handleGetDashboardListLoadedFromNeo4j(result){
58+
setRows(result);
59+
}
60+
4661
const reader = new FileReader();
4762
reader.onload = async (e) => {
4863
setText(e.target.result);
@@ -51,9 +66,29 @@ export const NeoLoadModal = ({ loadDashboard }) => {
5166
const uploadDashboard = async (e) => {
5267
e.preventDefault();
5368
reader.readAsText(e.target.files[0]);
54-
5569
}
5670

71+
const columns = [
72+
{ field: 'id', hide: true, headerName: 'ID', width: 150 },
73+
{ field: 'date', headerName: 'Date', width: 200 },
74+
{ field: 'title', headerName: 'Title', width: 270 },
75+
{ field: 'author', headerName: 'Author', width: 160 },
76+
{
77+
field: 'load', headerName: ' ', renderCell: (c) => {
78+
return <Button onClick={(e) => { loadDashboardFromNeo4j(driver, c.id, handleDashboardLoadedFromNeo4j) }} style={{ float: "right" }} variant="contained" size="medium" endIcon={<PlayArrow />}>Select</Button>
79+
}, width: 120
80+
},
81+
]
82+
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+
// ];
91+
5792
return (
5893
<div>
5994
<ListItem button onClick={handleClickOpen}>
@@ -65,7 +100,7 @@ export const NeoLoadModal = ({ loadDashboard }) => {
65100
<ListItemText primary="Load" />
66101
</ListItem>
67102

68-
<Dialog maxWidth={"lg"} open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
103+
<Dialog maxWidth={"lg"} open={loadModalOpen} onClose={handleClose} aria-labelledby="form-dialog-title">
69104
<DialogTitle id="form-dialog-title">
70105
<SystemUpdateAltIcon style={{
71106
height: "30px",
@@ -101,17 +136,13 @@ export const NeoLoadModal = ({ loadDashboard }) => {
101136
</Button>
102137
<Button
103138
component="label"
104-
// onClick={(e)=>uploadDashboard(e)}
139+
onClick={(e) => setLoadFromNeo4jModalOpen(true)}
105140
style={{ marginLeft: "10px", marginBottom: "10px", backgroundColor: "white" }}
106141
color="default"
107142
variant="contained"
108143
size="medium"
109144
endIcon={<StorageIcon />}>
110-
<input
111-
type="file"
112-
onChange={(e) => uploadDashboard(e)}
113-
hidden
114-
/>
145+
115146
Select From Neo4j
116147
</Button>
117148
<Button onClick={(text.length > 0) ? handleCloseAndLoad : null}
@@ -137,16 +168,45 @@ export const NeoLoadModal = ({ loadDashboard }) => {
137168
{/* <DialogActions> */}
138169
{/* </DialogActions> */}
139170
</Dialog>
171+
<Dialog maxWidth={"lg"} open={loadFromNeo4jModalOpen} onClose={(e) => { setLoadFromNeo4jModalOpen(false) }} aria-labelledby="form-dialog-title">
172+
<DialogTitle id="form-dialog-title">
173+
Load From Neo4j
174+
<IconButton onClick={(e) => { setLoadFromNeo4jModalOpen(false) }} style={{ padding: "3px", float: "right" }}>
175+
<Badge badgeContent={""} >
176+
<CloseIcon />
177+
</Badge>
178+
</IconButton>
179+
</DialogTitle>
180+
<DialogContent style={{ width: "800px" }}>
181+
<DialogContentText>If dashboards are saved in your current database, choose a dashboard below.
182+
</DialogContentText>
183+
184+
<div style={{ height: "360px" }}>
185+
<DataGrid
186+
rows={rows}
187+
columns={columns}
188+
pageSize={5}
189+
rowsPerPageOptions={[5]}
190+
disableSelectionOnClick
191+
components={{
192+
ColumnSortedDescendingIcon: () => <></>,
193+
ColumnSortedAscendingIcon: () => <></>,
194+
}}
195+
/></div>
196+
197+
</DialogContent>
198+
</Dialog>
140199
</div>
141200
);
142201
}
143202

144203
const mapStateToProps = state => ({
145-
146204
});
147205

148206
const mapDispatchToProps = dispatch => ({
149207
loadDashboard: text => dispatch(loadDashboardThunk(text)),
208+
loadDashboardFromNeo4j: (driver, uuid, callback) => dispatch(loadDashboardFromNeo4jThunk(driver, uuid, callback)),
209+
loadDashboardListFromNeo4j: (driver, callback) => dispatch(loadDashboardListFromNeo4jThunk(driver, callback)),
150210
});
151211

152212
//

src/modal/SaveModal.tsx

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { connect } from "react-redux";
1919
import { getDashboardJson } from './ModalSelectors';
2020
import { valueIsArray, valueIsObject } from '../report/RecordProcessing';
2121
import StorageIcon from '@material-ui/icons/Storage';
22+
import { applicationGetConnection } from '../application/ApplicationSelectors';
23+
import { saveDashboardToNeo4jThunk } from '../dashboard/DashboardThunks';
2224

2325
/**
2426
* A modal to save a dashboard as a JSON text string.
@@ -45,7 +47,6 @@ const filterNestedDict = (value: any, removedKeys: any[]) => {
4547
if (valueIsObject(value)) {
4648
const newValue = {};
4749
Object.keys(value).forEach(k => {
48-
4950
if (removedKeys.indexOf(k) != -1) {
5051
newValue[k] = undefined;
5152
} else {
@@ -56,15 +57,19 @@ const filterNestedDict = (value: any, removedKeys: any[]) => {
5657
}
5758
return value;
5859
}
59-
export const NeoSaveModal = ({ dashboard }) => {
60-
const [open, setOpen] = React.useState(false);
60+
61+
62+
63+
export const NeoSaveModal = ({ dashboard, connection, saveDashboardToNeo4j}) => {
64+
const [saveModalOpen, setSaveModalOpen] = React.useState(false);
65+
const [saveToNeo4jModalOpen, setSaveToNeo4jModalOpen] = React.useState(false);
6166

6267
const handleClickOpen = () => {
63-
setOpen(true);
68+
setSaveModalOpen(true);
6469
};
6570

6671
const handleClose = () => {
67-
setOpen(false);
72+
setSaveModalOpen(false);
6873
};
6974

7075
const filteredDashboard = filterNestedDict(dashboard, ["fields", "settingsOpen", "advancedSettingsOpen", "collapseTimeout"]);
@@ -89,7 +94,7 @@ export const NeoSaveModal = ({ dashboard }) => {
8994
<ListItemText primary="Save" />
9095
</ListItem>
9196

92-
<Dialog maxWidth={"lg"} open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
97+
<Dialog maxWidth={"lg"} open={saveModalOpen} onClose={handleClose} aria-labelledby="form-dialog-title">
9398
<DialogTitle id="form-dialog-title">
9499
<SaveIcon style={{
95100
height: "30px",
@@ -119,7 +124,7 @@ export const NeoSaveModal = ({ dashboard }) => {
119124
</Button>
120125
<Button
121126
component="label"
122-
onClick={downloadDashboard}
127+
onClick={(e) => { setSaveToNeo4jModalOpen(true) }}
123128
style={{ backgroundColor: "white", marginLeft: "10px" }}
124129
color="default"
125130
variant="contained"
@@ -139,16 +144,69 @@ export const NeoSaveModal = ({ dashboard }) => {
139144

140145
</DialogActions>
141146
</Dialog>
147+
148+
<Dialog maxWidth={"lg"} open={saveToNeo4jModalOpen} onClose={(e) => { setSaveToNeo4jModalOpen(false) }} aria-labelledby="form-dialog-title">
149+
<DialogTitle id="form-dialog-title">
150+
151+
Save to Neo4j
152+
153+
<IconButton onClick={(e) => { setSaveToNeo4jModalOpen(false) }} style={{ padding: "3px", float: "right" }}>
154+
<Badge badgeContent={""} >
155+
<CloseIcon />
156+
</Badge>
157+
</IconButton>
158+
</DialogTitle>
159+
<DialogContent style={{ width: "800px" }}>
160+
<DialogContentText>This will save your current dashboard as a node to your active Neo4j database.
161+
<br />Ensure you have write permissions to the database to use this feature.
162+
</DialogContentText>
163+
164+
<TextareaAutosize
165+
style={{ width: "100%", border: "1px solid lightgray" }}
166+
className={"textinput-linenumbers"}
167+
value={"{\n title: '" + dashboard.title + "',\n" +
168+
" date: '" + new Date().toString() + "',\n" +
169+
" user: '" + connection.username + "',\n" +
170+
" content: " + "{...}"+ "\n}" }
171+
aria-label=""
172+
placeholder="" />
173+
<Button
174+
component="label"
175+
onClick={e => {saveDashboardToNeo4j(dashboard, new Date().toString(), connection.username)}}
176+
style={{ backgroundColor: "white",marginTop: "20px", float: "right" }}
177+
color="default"
178+
variant="contained"
179+
endIcon={<SaveIcon />}
180+
size="medium">
181+
Save
182+
</Button>
183+
<Button
184+
component="label"
185+
onClick={(e) => { setSaveToNeo4jModalOpen(false) }}
186+
style={{ float: "right", marginTop: "20px", marginRight: "10px", ackgroundColor: "white" }}
187+
color="default"
188+
variant="contained"
189+
size="medium">
190+
Cancel
191+
</Button>
192+
</DialogContent>
193+
<DialogActions>
194+
195+
</DialogActions>
196+
</Dialog>
142197
</div>
143198
);
144199
}
145200

146201
const mapStateToProps = state => ({
147-
dashboard: getDashboardJson(state)
202+
dashboard: getDashboardJson(state),
203+
connection: applicationGetConnection(state)
148204
});
149205

150206
const mapDispatchToProps = dispatch => ({
151-
207+
saveDashboardToNeo4j: (dashboard: any, date: any, user: any) => {
208+
dispatch(saveDashboardToNeo4jThunk(dashboard, date, user))
209+
},
152210
});
153211

154212
//

0 commit comments

Comments
 (0)