Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 62 additions & 23 deletions src/components/DownloadDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import TextField from '@material-ui/core/TextField';
import csv from 'csv-stringify';
import { useLocalStorage } from '~/hooks';

const jsonToCsvString = (json) => new Promise((res, rej) => {
csv.stringify(json, (err, output) => {
Expand Down Expand Up @@ -104,31 +105,65 @@ const constructCsvObj = (message) => {
};

export default function DownloadDialog({
open, setOpen, message,
open, setOpen, message, download_type = 'answer',
}) {
const [type, setType] = React.useState('json');
const [fileName, setFileName] = React.useState('ROBOKOP_message');
const [queryHistory, setQueryHistory] = useLocalStorage('query_history', {});

const handleClose = () => {
setOpen(false);
};

const handleClickDownload = async () => {
let blob;
if (type === 'json') {
blob = new Blob([JSON.stringify({ message }, null, 2)], { type: 'application/json' });
}
if (type === 'csv') {
const csvString = await jsonToCsvString(constructCsvObj(message));
blob = new Blob([csvString], { type: 'text/csv' });
}
switch (download_type) {
case 'answer': {
let blob;
if (type === 'json') {
blob = new Blob([JSON.stringify({ message }, null, 2)], { type: 'application/json' });
}
if (type === 'csv') {
const csvString = await jsonToCsvString(constructCsvObj(message));
blob = new Blob([csvString], { type: 'text/csv' });
}

const a = document.createElement('a');
a.download = `${fileName}.${type}`;
a.href = window.URL.createObjectURL(blob);
document.body.appendChild(a);
a.click();
a.remove();
const a = document.createElement('a');
a.download = `${fileName}.${type}`;
a.href = window.URL.createObjectURL(blob);
document.body.appendChild(a);
a.click();
a.remove();
break;
}
case 'all_queries': {
const raw = window.localStorage.getItem('query_history');
const parsed = raw ? JSON.parse(raw) : {};
const blob = new Blob([JSON.stringify({ bookmarked_queries: parsed }, null, 2)], { type: 'application/json' });
// const blob = new Blob([JSON.stringify({ queryHistory }, null, 2)], { type: 'application/json' });
const a = document.createElement('a');
a.download = `${fileName}.${type}`;
a.href = window.URL.createObjectURL(blob);
document.body.appendChild(a);
a.click();
a.remove();
break;
}
case 'query': {
// Bookmark the query with the filename that's given.
if (!(fileName in queryHistory)) {
setQueryHistory((prev) => ({
...prev,
[fileName]: {
query_graph: message,
},
}));
}
break;
}
default: {
handleClose();
}
}

handleClose();
};
Expand All @@ -142,20 +177,24 @@ export default function DownloadDialog({
<DialogTitle id="alert-dialog-title">Download Answer</DialogTitle>
<DialogContent style={{ width: 600 }}>
<TextField
label="File name"
label={['answer', 'all_queries'].includes(download_type) ? 'File name' : 'Query Graph Name'}
fullWidth
variant="outlined"
style={{ marginBottom: '2rem' }}
value={fileName}
onChange={(e) => { setFileName(e.target.value); }}
/>

<FormControl component="fieldset">
<RadioGroup aria-label="gender" name="gender1" value={type} onChange={(e) => { setType(e.target.value); }}>
<FormControlLabel value="json" control={<Radio />} label="JSON" />
<FormControlLabel value="csv" control={<Radio />} label="CSV" />
</RadioGroup>
</FormControl>
{ // Show the radio group only when the download type is answers.
download_type === 'answer' && (
<FormControl component="fieldset">
<RadioGroup aria-label="gender" name="gender1" value={type} onChange={(e) => { setType(e.target.value); }}>
<FormControlLabel value="json" control={<Radio />} label="JSON" />
<FormControlLabel value="csv" control={<Radio />} label="CSV" />
</RadioGroup>
</FormControl>
)
}

{
type === 'csv' && (
Expand All @@ -170,7 +209,7 @@ export default function DownloadDialog({
Cancel
</Button>
<Button onClick={handleClickDownload} color="primary" variant="contained">
Download
{['answer', 'all_queries'].includes(download_type) ? 'Download' : 'Bookmark'}
</Button>
</DialogActions>
</Dialog>
Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-local-storage';
41 changes: 41 additions & 0 deletions src/hooks/use-local-storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useState } from 'react';

export const useLocalStorage = (key, initialValue) => {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});

// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};

return [storedValue, setValue];
};

export default {
useLocalStorage,
};
18 changes: 18 additions & 0 deletions src/pages/answer/leftDrawer/LeftDrawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ export default function LeftDrawer({
<ListItemText primary={val.label} />
</ListItem>
))}
<ListItem
component="label"
button
disabled={!Object.keys(message).length}
onClick={() => { setDownloadOpen(true); }}
>
<ListItemIcon>
<IconButton
component="span"
style={{ fontSize: '18px' }}
title="Download"
disableRipple
>
<GetAppIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Download Query" />
</ListItem>
<ListItem
component="label"
button
Expand Down
2 changes: 1 addition & 1 deletion src/pages/answer/resultsTable/resultsTable.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
}

#resultJSONContainer {
max-height: 300px;
max-height: 50pc;
overflow-y: auto;
}

Expand Down
15 changes: 14 additions & 1 deletion src/pages/queryBuilder/QueryBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import GraphEditor from './graphEditor/GraphEditor';
import TextEditor from './textEditor/TextEditor';
import JsonEditor from './jsonEditor/JsonEditor';
import TemplatedQueriesModal from './templatedQueries/TemplatedQueriesModal';
import DownloadDialog from '~/components/DownloadDialog';

import './queryBuilder.css';

Expand All @@ -41,6 +42,7 @@ export default function QueryBuilder() {
const queryBuilder = useQueryBuilder();
const pageStatus = usePageStatus(false);
const [showJson, toggleJson] = useState(false);
const [downloadOpen, setDownloadOpen] = useState(false);
const [ara] = useState(ARAs[0]);
const displayAlert = useContext(AlertContext);
const history = useHistory();
Expand Down Expand Up @@ -203,13 +205,18 @@ export default function QueryBuilder() {
open={exampleQueriesOpen}
setOpen={setExampleQueriesOpen}
/>

<Button
onClick={() => toggleJson(true)}
variant="outlined"
>
Edit JSON
</Button>
<Button
onClick={() => setDownloadOpen(true)}
variant="outlined"
>
Download Query
</Button>
<SubmitButton
onClick={onQuickSubmit}
variant="contained"
Expand All @@ -231,6 +238,12 @@ export default function QueryBuilder() {
show={showJson}
close={() => toggleJson(false)}
/>
<DownloadDialog
open={downloadOpen}
setOpen={setDownloadOpen}
message={queryBuilder.query_graph}
download_type="all_queries"
/>
</QueryBuilderContext.Provider>
</div>
</div>
Expand Down
21 changes: 20 additions & 1 deletion src/pages/queryBuilder/graphEditor/GraphEditor.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
useContext, useReducer, useEffect,
useContext, useReducer, useEffect, useState,
} from 'react';
import Popover from '@material-ui/core/Popover';
import Button from '@material-ui/core/Button';
Expand All @@ -11,6 +11,7 @@ import nodeUtils from '~/utils/d3/nodes';
import QueryGraph from './QueryGraph';
import NodeSelector from '../textEditor/textEditorRow/NodeSelector';
import PredicateSelector from '../textEditor/textEditorRow/PredicateSelector';
import DownloadDialog from '~/components/DownloadDialog';

import './graphEditor.css';

Expand Down Expand Up @@ -68,6 +69,7 @@ function clickReducer(state, action) {
export default function GraphEditor() {
const queryBuilder = useContext(QueryBuilderContext);
const { query_graph } = queryBuilder;
const [downloadOpen, setDownloadOpen] = useState(false);

const [clickState, clickDispatch] = useReducer(clickReducer, {
creatingConnection: false,
Expand Down Expand Up @@ -144,6 +146,17 @@ export default function GraphEditor() {
>
Connect Terms
</Button>
<Button
onClick={() => {
setDownloadOpen(true);
// auto close after 5 seconds
setTimeout(() => {
clickDispatch({ type: 'closeEditor' });
}, 5000);
}}
>
Bookmark Graph
</Button>
</div>
<Popover
open={Boolean(clickState.popoverAnchor)}
Expand Down Expand Up @@ -180,6 +193,12 @@ export default function GraphEditor() {
</Paper>
)}
</Popover>
<DownloadDialog
open={downloadOpen}
setOpen={setDownloadOpen}
message={queryBuilder.query_graph}
download_type="query"
/>
</div>
</div>
);
Expand Down
44 changes: 42 additions & 2 deletions src/pages/queryBuilder/templatedQueries/TemplatedQueriesModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Close } from '@material-ui/icons';
import QueryBuilderContext from '~/context/queryBuilder';
import examples from './templates.json';
import NodeSelector from '../textEditor/textEditorRow/NodeSelector';
import { useLocalStorage } from '~/hooks';

const useStyles = makeStyles((theme) => ({
modal: {
Expand Down Expand Up @@ -89,9 +90,10 @@ export default function TemplatedQueriesModal({
}) {
const classes = useStyles();
const queryBuilder = useContext(QueryBuilderContext);

const [selectedExample, setSelectedExample] = useState(null);

const raw = window.localStorage.getItem('query_history');
const bookmarked_queries = raw ? JSON.parse(raw) : null;
console.log(bookmarked_queries);
const handleClose = () => {
setOpen(false);
setSelectedExample(null);
Expand All @@ -107,6 +109,24 @@ export default function TemplatedQueriesModal({
queryBuilder.dispatch({ type: 'editNode', payload: { id, node } });
};

const handleSelectBookmarkedQuery = (query_graph) => {
const example = {
template: [
{
text: JSON.stringify(query_graph.query_graph, null, 2),
type: 'json_text',
},
],
};
console.log('In handeSelectBookmarkedQuery');
console.log(query_graph);
setSelectedExample(example);
const payload = {
message: query_graph,
};
queryBuilder.dispatch({ type: 'saveGraph', payload });
};

return (
<Modal open={open} onClose={handleClose} className={classes.modal}>
<div className={classes.paper}>
Expand Down Expand Up @@ -143,6 +163,23 @@ export default function TemplatedQueriesModal({
/>
</ListItem>
))}
{bookmarked_queries && Object.entries(bookmarked_queries).map(([key, value], i) => (
<ListItem
button
divider
key={`bookmark-${i}`}
onClick={() => handleSelectBookmarkedQuery(value)}
>
<ListItemText
primary={(
<>
<Chip size="small" label="Bookmarked" color="secondary" />{' '}
{key}
</>
)}
/>
</ListItem>
))}
</List>
<Divider orientation="vertical" flexItem />
<div
Expand Down Expand Up @@ -194,6 +231,9 @@ export default function TemplatedQueriesModal({
</div>
);
}
if (part.type === 'json_text') {
return <pre id="resultJSONContainer">{part.text}</pre>;
}
return null;
})
}
Expand Down
Loading