Skip to content

Commit 47fa4d8

Browse files
committed
feat(history): save and load query history
recaftor(ToolScreen & processForm
1 parent f421742 commit 47fa4d8

File tree

15 files changed

+579
-108
lines changed

15 files changed

+579
-108
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
"private": true,
55
"dependencies": {
66
"axios": "^0.18.0",
7+
"bluebird": "^3.5.2",
78
"immutable": "^3.8.2",
89
"leaflet": "^1.3.3",
910
"lodash": "^4.17.10",
11+
"moment": "^2.22.2",
1012
"prop-types": "^15.6.2",
1113
"react": "^16.4.2",
1214
"react-dom": "^16.4.2",

src/App.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
1-
import React, { Component } from 'react';
1+
import React, { Component } from "react";
22

3-
import Map from './containers/MapContainer';
4-
import ToolsScreen from './components/ToolsScreen';
5-
import Layers from './components/Layers';
3+
import Map from "./containers/MapContainer";
4+
import ToolsScreen from "./components/ToolsScreen";
5+
import Layers from "./components/Layers";
66

7-
import './App.css';
7+
import "./App.css";
8+
import { setQueryHistory, getQueryHistory } from "./utils/queryHistoryStorage";
89

910
class App extends Component {
10-
constructor(){
11+
constructor() {
1112
super();
1213

1314
this.state = {
14-
addLayer: () => this.layers.addLayer,
15-
}
15+
addLayer: () => this.layers.addLayer
16+
};
1617
}
1718

1819
render() {
1920
return (
20-
<div className='root'>
21-
<div className='screen'>
22-
<Layers onRef = {ref => (this.layers = ref)}/>
23-
<Map/>
21+
<div className="root">
22+
<div className="screen">
23+
<Layers onRef={ref => (this.layers = ref)} />
24+
<Map />
2425
</div>
25-
<div className='screen'>
26-
<ToolsScreen addLayer={this.state.addLayer}/>
26+
<div className="screen">
27+
<ToolsScreen
28+
addLayer={this.state.addLayer}
29+
setQueryHistory={setQueryHistory}
30+
getQueryHistory={getQueryHistory}
31+
/>
2732
</div>
2833
</div>
2934
);

src/components/LayerInput/LayerInput.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from "react";
22
import PropTypes from "prop-types";
3-
import axios from "axios";
43
import Select from "react-select";
54

65
import "./LayerInput.css";
@@ -18,7 +17,7 @@ class LayerInput extends React.Component {
1817
}
1918

2019
handleSelect({ value }) {
21-
this.props.addLayer(value);
20+
this.props.onSelect(value, this.props.id);
2221
}
2322

2423
render() {
@@ -35,7 +34,6 @@ class LayerInput extends React.Component {
3534
}
3635

3736
LayerInput.propTypes = {
38-
addLayer: PropTypes.func.isRequired,
3937
onSelect: PropTypes.func
4038
};
4139

src/components/Layers/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export {default} from './Layers';
1+
export { default } from "./Layers";
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default class Input {
2+
constructor(id, type, title, abstract, minOccurs, maxOccurs, values = []) {
3+
this.id = id;
4+
this.type = type;
5+
this.title = title;
6+
this.abstract = abstract;
7+
this.minOccurs = minOccurs;
8+
this.maxOccurs = maxOccurs;
9+
this.values = values;
10+
}
11+
}
Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import React from 'react';
2-
import PropTypes from 'prop-types';
3-
import _ from 'lodash';
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import _ from "lodash";
4+
import Input from "./Input";
5+
import { InputTypes } from "./ProcessFormUtils";
46

5-
import LayerInput from '../LayerInput';
7+
import LayerInput from "../LayerInput";
68
// import LayerInput from '../../containers/LayerInputContainer'; todo remove LayerInputContainer
79

8-
import './ProcessForm.css';
9-
10-
const MIME_TYPE = 'application/vnd.geo+json';
10+
import "./ProcessForm.css";
1111

1212
class ProcessForm extends React.Component {
13-
constructor (props) {
13+
constructor(props) {
1414
super(props);
1515

1616
this.inputValues = [];
@@ -19,44 +19,63 @@ class ProcessForm extends React.Component {
1919
this.inputGenerator = new window.InputGenerator();
2020
}
2121

22-
handleAddComplexData (identifier, value) {
23-
// const value = event.target.value;
24-
_.pullAllBy(this.inputValues, [{'identifier': identifier}], 'identifier');
25-
const newInput = this.inputGenerator.createComplexDataInput_wps_1_0_and_2_0(
26-
identifier, MIME_TYPE, undefined, undefined, true, value);
27-
this.inputValues.push(newInput);
28-
// this.props.addLayer('614', event.target.value, 'LAyerName');
22+
handleAddComplexData(layerId, i) {
23+
const values = layerId !== undefined ? [layerId] : [];
24+
25+
const identifier = this.props.inputs[i].id;
26+
let inputs = this.props.inputs.map(
27+
input =>
28+
input.id === identifier
29+
? Object.assign(new Input(), input, { values: [layerId] })
30+
: input
31+
);
32+
33+
this.props.onChange(inputs);
2934
}
3035

31-
handleAddLiteralData (identifier, event) {
36+
handleAddLiteralData(event) {
37+
const i = parseInt(event.currentTarget.id.replace("form-input-", ""));
38+
const identifier = this.props.inputs[i].id;
3239
const value = event.target.value;
33-
_.pullAllBy(this.inputValues, [{'identifier': identifier}], 'identifier');
34-
const newInput = this.inputGenerator.createLiteralDataInput_wps_1_0_and_2_0(
35-
identifier, undefined, undefined, value);
36-
this.inputValues.push(newInput);
37-
// this.props.addLayer('614', event.target.value, 'LAyerName');
40+
41+
const values = value ? [value] : [];
42+
let inputs = this.props.inputs.map(
43+
input =>
44+
input.id === identifier
45+
? Object.assign(new Input(), input, { values })
46+
: input
47+
);
48+
49+
this.props.onChange(inputs);
3850
}
3951

40-
render () {
41-
const {inputs, handleOnSubmit} = this.props;
52+
render() {
53+
const { inputs, handleOnSubmit } = this.props;
4254
return (
4355
<form>
44-
{inputs.map(input =>
56+
{inputs.map((formInput, i) => (
4557
<div>
46-
<h3>{input.title}</h3>
47-
<p>{input.abstractValue}</p>
48-
{input.hasOwnProperty('complexData')
49-
? <LayerInput
58+
<h3>{formInput.title}</h3>
59+
<p>{formInput.abstractValue}</p>
60+
{formInput.type === InputTypes.COMPLEX ? (
61+
<LayerInput
5062
layers={this.props.layers}
51-
key={input.identifier}
52-
addLayer={this.handleAddComplexData.bind(null,
53-
input.identifier)}/>
54-
: <input key={input.identifier}
55-
onBlur={this.handleAddLiteralData.bind(null,
56-
input.identifier)}
57-
type="text"/>}
58-
</div>)}
59-
<button type="button" onClick={() => handleOnSubmit(this.inputValues)}>
63+
key={i}
64+
id={i}
65+
onSelect={this.handleAddComplexData}
66+
/>
67+
) : (
68+
<input
69+
key={formInput.identifier}
70+
onChange={this.handleAddLiteralData}
71+
value={formInput.values[0]}
72+
id={`form-input-${i}`}
73+
type="text"
74+
/>
75+
)}
76+
</div>
77+
))}
78+
<button type="button" onClick={this.props.onSubmit}>
6079
Execute
6180
</button>
6281
</form>
@@ -66,7 +85,7 @@ class ProcessForm extends React.Component {
6685

6786
ProcessForm.propTypes = {
6887
inputs: PropTypes.array.isRequired,
69-
handleOnSubmit: PropTypes.func.isRequired,
88+
onSubmit: PropTypes.func.isRequired
7089
};
7190

72-
export default ProcessForm;
91+
export default ProcessForm;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Input from "./Input";
2+
3+
/**
4+
* @param {object} processDescription
5+
* @returns {Input}
6+
*/
7+
export function generateInputsFromDescription(process) {
8+
return process.inputs.map(input => {
9+
const { abstractValue, title, identifier, minOccurs, maxOccurs } = input;
10+
let type = input.hasOwnProperty("complexData")
11+
? InputTypes.COMPLEX
12+
: InputTypes.LITERAL;
13+
14+
return new Input(identifier, type, title, abstractValue, minOccurs, maxOccurs);
15+
16+
});
17+
}
18+
19+
export const InputTypes = {
20+
COMPLEX: 0,
21+
LITERAL: 1
22+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* .query-history {
2+
} */
3+
4+
.query-history .query-record {
5+
padding: 5px 18px;
6+
background-color: #eee;
7+
margin: 7px 12px;
8+
transition: background-color 0.5s ease;
9+
position: relative;
10+
}
11+
12+
.query-history .query-record:hover {
13+
background-color: #ddd;
14+
}
15+
16+
.query-history .query-record .request-time {
17+
position: absolute;
18+
right: 25px;
19+
top: 20px;
20+
}
21+
22+
.query-history .query-record .status-tag {
23+
display: inline-block;
24+
width: 80px;
25+
font-weight: bold;
26+
color: white;
27+
padding: 2px 3px;
28+
border-radius: 3px;
29+
text-align: center;
30+
}
31+
32+
.query-history .query-record .title {
33+
display: inline-block;
34+
font-size: 1.17em;
35+
margin-block-start: 1em;
36+
margin-block-end: 1em;
37+
margin-inline-start: 0px;
38+
margin-inline-end: 0px;
39+
margin-left: 0.5em;
40+
font-weight: bold;
41+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
4+
import moment from "moment";
5+
6+
import { QueryRecord, Statuses } from "./QueryRecord";
7+
import "./QueryHistory.css";
8+
9+
const StatusDisplayMap = {
10+
[Statuses.SUCCESS]: {
11+
color: "green",
12+
text: "Success"
13+
},
14+
[Statuses.FAIL]: {
15+
color: "#cc5858",
16+
text: "Failed"
17+
},
18+
[Statuses.RUNNING]: {
19+
color: "lightblue",
20+
text: "Running"
21+
},
22+
[Statuses.SAVE_WITHOUT_RUN]: {
23+
color: "lightblue",
24+
text: "Saved"
25+
}
26+
};
27+
28+
export default class QueryHistory extends React.Component {
29+
constructor(props) {
30+
super(props);
31+
32+
this.onRecordClick = this.onRecordClick.bind(this);
33+
}
34+
35+
onRecordClick(e) {
36+
const id = e.currentTarget.id.replace("query-record-", "");
37+
return this.props.onClick(
38+
this.props.history.filter(rec => rec.queryId === id)[0]
39+
);
40+
}
41+
42+
43+
getStatusTag(status){
44+
const statusDisplay = StatusDisplayMap[status] || StatusDisplayMap[Statuses.FAIL];
45+
return <span className="status-tag" style={{backgroundColor:statusDisplay.color}}>{statusDisplay.text}</span>
46+
}
47+
render() {
48+
return (
49+
<div className="query-history">
50+
{this.props.history.map(a => (
51+
<div
52+
key={a.queryId}
53+
id={`query-record-${a.queryId}`}
54+
className="query-record"
55+
onClick={this.onRecordClick}
56+
>
57+
<div>
58+
{this.getStatusTag(a.status)}
59+
<span className="title">{a.title}</span>
60+
<span className="request-time">
61+
{moment(a.timestamp).fromNow()}
62+
</span>
63+
</div>
64+
<h4>{`${a.inputs.length} inputs, ${a.outputs.length} output`}</h4>
65+
<span className="request-time">
66+
{moment(a.timestamp).fromNow()}
67+
</span>
68+
</div>
69+
))}
70+
</div>
71+
);
72+
}
73+
}
74+
75+
QueryHistory.propTypes = {
76+
history: PropTypes.arrayOf(PropTypes.instanceOf(QueryRecord)),
77+
onClick: PropTypes.func
78+
};

0 commit comments

Comments
 (0)