Commit 86d9b01a authored by Oleksii Savran's avatar Oleksii Savran Committed by Matija Obreza

Remember map

parent ee446e88
......@@ -320,7 +320,8 @@
"Not found": "Not found",
"Nothing matches your request": "Nothing matches your request",
"label": {
"metadata": "Record metadata"
"metadata": "Record metadata",
"color": "Color"
},
"footer": {
"About Genesys": "About Genesys",
......@@ -439,7 +440,10 @@
"filterAccessions": "Filter accessions",
"pick": "Show climate",
"stopPick": "Cancel",
"noClimateData": "No climate data available for selected location"
"noClimateData": "No climate data available for selected location",
"rememberMap": "Remember map",
"mapList": "Map list",
"savedMaps": "Saved maps"
}
}
},
......
......@@ -243,6 +243,11 @@
"integrity": "sha512-pH4KCsbtBLLe7eqUrw8brcuFO8IZlN36JjdKlOublibVdAIPHCzEnpBWOVUXK5sCf+DpBi8ZtuWtjF0srybdeA==",
"dev": true
},
"@icons/material": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
},
"@material-ui/core": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.4.0.tgz",
......@@ -5157,9 +5162,9 @@
"dev": true
},
"fsevents": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
"integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
"integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
"dev": true,
"optional": true,
"requires": {
......@@ -5186,7 +5191,7 @@
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"version": "1.1.5",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5212,7 +5217,7 @@
}
},
"chownr": {
"version": "1.0.1",
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
......@@ -5251,7 +5256,7 @@
}
},
"deep-extend": {
"version": "0.5.1",
"version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true
......@@ -5300,7 +5305,7 @@
}
},
"glob": {
"version": "7.1.2",
"version": "7.1.3",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5320,12 +5325,12 @@
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"version": "0.4.24",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
......@@ -5390,17 +5395,17 @@
"optional": true
},
"minipass": {
"version": "2.2.4",
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.1.0",
"version": "1.2.1",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5424,7 +5429,7 @@
"optional": true
},
"needle": {
"version": "2.2.0",
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5435,18 +5440,18 @@
}
},
"node-pre-gyp": {
"version": "0.10.0",
"version": "0.10.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.0",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.1.7",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
......@@ -5463,13 +5468,13 @@
}
},
"npm-bundled": {
"version": "1.0.3",
"version": "1.0.5",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true,
......@@ -5546,12 +5551,12 @@
"optional": true
},
"rc": {
"version": "1.2.7",
"version": "1.2.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
......@@ -5581,16 +5586,16 @@
}
},
"rimraf": {
"version": "2.6.2",
"version": "2.6.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"glob": "^7.0.5"
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.1",
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
......@@ -5608,7 +5613,7 @@
"optional": true
},
"semver": {
"version": "5.5.0",
"version": "5.6.0",
"bundled": true,
"dev": true,
"optional": true
......@@ -5661,17 +5666,17 @@
"optional": true
},
"tar": {
"version": "4.4.1",
"version": "4.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.0.1",
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.2.4",
"minizlib": "^1.1.0",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
......@@ -5682,12 +5687,12 @@
"optional": true
},
"wide-align": {
"version": "1.1.2",
"version": "1.1.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"string-width": "^1.0.2"
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
......@@ -5697,7 +5702,7 @@
"optional": true
},
"yallist": {
"version": "3.0.2",
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
......@@ -7908,6 +7913,11 @@
"integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==",
"dev": true
},
"material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
},
"math-expression-evaluator": {
"version": "1.2.17",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
......@@ -13502,6 +13512,19 @@
"load-script": "^1.0.0"
}
},
"react-color": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.0.tgz",
"integrity": "sha512-kJfE5tSaFe6GzalXOHksVjqwCPAsTl+nzS9/BWfP7j3EXbQ4IiLAF9sZGNzk3uq7HfofGYgjmcUgh0JP7xAQ0w==",
"requires": {
"@icons/material": "^0.2.4",
"lodash": ">4.17.4",
"material-colors": "^1.2.1",
"prop-types": "^15.5.10",
"reactcss": "^1.2.0",
"tinycolor2": "^1.4.1"
}
},
"react-create-ref": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-create-ref/-/react-create-ref-1.0.1.tgz",
......@@ -13961,6 +13984,14 @@
"prop-types": "^15.6.2"
}
},
"reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
"requires": {
"lodash": "^4.0.1"
}
},
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
......@@ -16627,6 +16658,11 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"to-absolute-glob": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
......
......@@ -2,7 +2,12 @@
"name": "genesys-pgr-ui",
"version": "1.0.0",
"description": "Genesys Website",
"keywords": [ "genebank", "genesys-pgr", "PGRFA", "accession" ],
"keywords": [
"genebank",
"genesys-pgr",
"PGRFA",
"accession"
],
"license": "Apache-2.0",
"homepage": "https://gitlab.croptrust.org/genesys-pgr/genesys-ui.git",
"repository": {
......@@ -75,6 +80,7 @@
"react-autosuggest": "^9.3.4",
"react-chartkick": "^0.3.0",
"react-ckeditor-component": "^1.1.0",
"react-color": "^2.17.0",
"react-dnd": "^7.0.2",
"react-dnd-html5-backend": "^7.0.2",
"react-dom": "^16.4.1",
......
......@@ -18,6 +18,8 @@ import {
RECEIVE_TILE_LAYER,
APPEND_ACCESSIONS,
APPEND_ACCESSIONS_WITH_SUGGESTIONS,
INIT_MY_MAPS, ADD_MY_MAP,
TOGGLE_MY_MAP,
} from 'accessions/constants';
import AccessionService from 'service/genesys/AccessionService';
import ClimateService from 'service/genesys/ClimateService';
......@@ -222,4 +224,39 @@ export const applyClimateFilters = (climate: TileClimate, extraFilters?: any) =>
dispatch(applyFilters(filter));
};
const initSavedMaps = (savedMaps: any) => ({
type: INIT_MY_MAPS,
payload: savedMaps,
});
const addMap = (map: any) => ({
type: ADD_MY_MAP,
payload: map,
});
export const initMyMaps = () => (dispatch) => {
if (typeof window !== 'undefined') {
const myMaps = JSON.parse(window.localStorage.getItem('myMaps'));
if (myMaps && myMaps.length !== 0) {
const maps = myMaps.map((el) => {
el.enabled = false;
return el;
});
return dispatch(initSavedMaps(maps));
}
}
};
export const addToMyMaps = (map: any) => (dispatch, getState) => {
const myMaps = getState().accessions.public.myMaps;
if (!myMaps.some((el) => el.filterCode === map.filterCode)) {
dispatch(addMap(map));
window.localStorage.setItem('myMaps', JSON.stringify(getState().accessions.public.myMaps));
} else {
console.log(`Can't add map with filter ${map.filterCode}. Already in myMaps`);
}
};
export const toggleMyMap = (filterCode: string, enabled: boolean) => (dispatch) => {
dispatch({type: TOGGLE_MY_MAP, payload: {filterCode, enabled}});
};
......@@ -9,6 +9,11 @@ export const ACCESSION_FILTERFORM = 'Form/Accession/ACCESSION_FILTERFORM';
export const ACCESSION_MAP_FILTERFORM = 'Form/Accession/ACCESSION_MAP_FILTERFORM';
export const ACCESSION_FORM = 'Form/Accession/ACCESSION_FORM';
export const ACCESSION_REMEMBERMAPFORM = 'Form/Accession/ACCESSION_REMEMBERMAPFORM';
export const INIT_MY_MAPS = 'accessions/INIT_MY_MAPS';
export const ADD_MY_MAP = 'accessions/ADD_MY_MAP';
export const TOGGLE_MY_MAP = 'accessions/TOGGLE_MY_MAP';
// Dashboard
export const DASHBOARD_APPEND_ACCESSIONS = 'accessions/dashboard/APPEND_ACCESSIONS';
export const DASHBOARD_RECEIVE_ACCESSION = 'accessions/dashboard/RECEIVE_ACCESSION';
......@@ -8,6 +8,7 @@ import {
RECEIVE_ACCESSION_MAPINFO,
RECEIVE_ACCESSION_AUDIT_LOG,
RECEIVE_TILE_LAYER, APPEND_ACCESSIONS_WITH_SUGGESTIONS,
INIT_MY_MAPS, ADD_MY_MAP, TOGGLE_MY_MAP,
} from 'accessions/constants';
import FilteredPage from 'model/FilteredPage';
......@@ -25,7 +26,8 @@ const INITIAL_STATE: {
suggestions: any;
overview: ApiCall<AccessionOverview>;
mapInfo: ApiCall<AccessionMapInfo>;
mapLayers: MapLayer[]
mapLayers: MapLayer[];
myMaps: any;
} = {
accession: null,
auditLog: null,
......@@ -34,6 +36,7 @@ const INITIAL_STATE: {
overview: null,
mapInfo: null,
mapLayers: AVAILABLE_LAYERS,
myMaps: [],
};
function publicAccessions(state = INITIAL_STATE, action: IReducerAction) {
......@@ -137,6 +140,25 @@ function publicAccessions(state = INITIAL_STATE, action: IReducerAction) {
},
});
}
case INIT_MY_MAPS: {
return update(state, {
myMaps: { $set: action.payload },
});
}
case ADD_MY_MAP: {
return update(state, {
myMaps: { $push: [action.payload] },
});
}
case TOGGLE_MY_MAP: {
const {filterCode, enabled} = action.payload;
const indexToUpdate = state.myMaps.findIndex((item) => item.filterCode === filterCode);
return update(state, {
myMaps: {
[indexToUpdate]: { enabled: { $set: enabled } },
},
});
}
default:
return state;
......
......@@ -77,9 +77,11 @@
"filterAccessions": "Filter accessions",
"pick": "Show climate",
"stopPick": "Cancel",
"noClimateData": "No climate data available for selected location"
"noClimateData": "No climate data available for selected location",
"rememberMap": "Remember map",
"mapList": "Map list",
"savedMaps": "Saved maps"
}
}
},
"tab": {
......
This diff is collapsed.
import * as React from 'react';
import {translate} from 'react-i18next';
import { Field, reduxForm } from 'redux-form';
import { ACCESSION_REMEMBERMAPFORM } from 'accessions/constants';
import Validators from 'utilities/Validators';
import withStyles from '@material-ui/core/styles/withStyles';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Button from '@material-ui/core/Button';
import {TextField} from 'ui/common/text-field/index';
import ColorPicker from 'ui/common/forms/ColorPicker';
import ExpandFiltersComponent from 'ui/common/filter/ExpandFiltersComponent';
import CollapsibleComponentSearch from 'ui/common/filter/CollapsibleComponentSearch';
/*tslint:disable*/
const styles = (theme) => ({
root: {
overflow: 'hidden' as 'hidden',
width: '270px',
},
savedItem: {
display: 'flex',
justifyContent: 'space-between' as 'space-between',
alignItems: 'center' as 'center',
},
colorLegend: {
height: '18px',
width: '30px',
border: 'solid 2px rgba(0, 0, 0, 0.57)',
borderRadius: '3px',
boxSizing: 'border-box' as 'border-box',
}
});
interface IMapSavedSectionProps extends React.ClassAttributes<any> {
classes: any;
t: any;
submitting: boolean;
invalid: boolean;
toggleInputFocus: (focused: boolean) => void;
handleSubmit: any;
filterCode: string;
myMaps: any[];
addToMyMaps: (map: any) => void;
toggleMyMap: (filterCode: string, enabled: boolean) => void;
reset: () => void;
}
class MapSavedSection extends React.Component<IMapSavedSectionProps> {
private onFocus = () => {
this.props.toggleInputFocus(true);
};
private onLoseFocus = () => {
this.props.toggleInputFocus(false);
};
private processSubmit = this.props.handleSubmit((values) => {
const { addToMyMaps, filterCode, reset } = this.props;
addToMyMaps({
name: values.name,
color: values.color.slice(1),
filterCode: filterCode,
enabled: true
});
reset();
});
private onCheckboxChange = (filterCode, en) => (e, value) => {
this.props.toggleMyMap(filterCode, value);
};
public render() {
const { classes, t, submitting, invalid, toggleInputFocus, myMaps } = this.props;
return (
<div className={ classes.root }>
<ExpandFiltersComponent title={ t('accessions.public.p.map.savedMaps') }/>
<CollapsibleComponentSearch title={ t('accessions.public.p.map.rememberMap') } collapsed>
<div>
<form
onFocus={ toggleInputFocus && this.onFocus }
onBlur={ toggleInputFocus && this.onLoseFocus }
onSubmit={this.processSubmit}
>
<Field
name="name"
label={ t('name') }
type="text"
component={ TextField }
validate={ [Validators.required] }
fullWidth
/>
<Field
name="color"
type="color"
component={ ColorPicker }
validate={ [Validators.required] }
fullWidth
/>
<Button variant="contained" type="submit" className="mt-1rem mr-1rem" disabled={ submitting || invalid }>
{ t('common:action.save') }
</Button>
</form>
</div>
</CollapsibleComponentSearch>
<CollapsibleComponentSearch
title={ t('accessions.public.p.map.mapList') } // TODO: add disabled when myMaps.length === 0
collapsed
>
{ myMaps.map((map) => {
return (
<div className={ classes.savedItem }>
<FormControlLabel
className={ classes.statusCheckbox }
label={ map.name }
control={
<Checkbox
checked={ map.enabled }
onChange={ this.onCheckboxChange(map.filterCode, map.enabled) }
/>
}
/>
<div className={ classes.colorLegend } style={ {backgroundColor: `#${map.color}`} }/>
</div>
);
}) }
</CollapsibleComponentSearch>
</div>
);
}
}
export default translate()(reduxForm({
enableReinitialize: true,
destroyOnUnmount: false,
form: ACCESSION_REMEMBERMAPFORM,
})(withStyles(styles)(MapSavedSection)));
......@@ -320,7 +320,8 @@
"Not found": "Not found",
"Nothing matches your request": "Nothing matches your request",
"label": {
"metadata": "Record metadata"
"metadata": "Record metadata",
"color": "Color"
},
"footer": {
"About Genesys": "About Genesys",
......
......@@ -5,6 +5,7 @@ import { bindActionCreators } from 'redux';
import { updateHistory } from 'actions/history';
import { loadCrops } from 'crop/actions/public';
import { initMyList } from 'list/actions/public';
import { initMyMaps } from 'accessions/actions/public';
import { checkSoftwareVersion, serverInfoRequest } from 'actions/serverInfo';
import { withRouter } from 'react-router-dom';
......@@ -31,10 +32,16 @@ interface IAppProps extends React.ClassAttributes<any> {
serverInfoRequest: any;
checkSoftwareVersion: any;
tReady: any;
initMyMaps: any;
}
class App extends React.Component<IAppProps, any> {
public static needs = [
() => loadCrops(),
() => serverInfoRequest(),
];
public constructor(props: any) {
super(props);
}
......@@ -51,8 +58,9 @@ class App extends React.Component<IAppProps, any> {
}
public componentDidMount() {
const { initMyList, checkSoftwareVersion } = this.props;
const { initMyList, checkSoftwareVersion, initMyMaps } = this.props;
initMyList();
initMyMaps();
console.log('Loading software version');
checkSoftwareVersion();
}
......@@ -88,6 +96,7 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
initMyList,
serverInfoRequest,
checkSoftwareVersion,
initMyMaps,
}, dispatch);
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
import * as React from 'react';
import {translate} from 'react-i18next';
import FormControl from 'ui/common/forms/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
import { HuePicker } from 'react-color';