Commit 264cf708 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '384-location-editor-and-display' into 'master'

Resolve "Location editor and display"

Closes #384

See merge request !309
parents 6c21fec8 18382802
Pipeline #7131 passed with stages
in 5 minutes and 35 seconds
......@@ -7,6 +7,7 @@ import { IGeoPoint } from 'model/common.model';
import DatasetLocation from 'model/catalog/DatasetLocation';
import { Properties, PropertiesItem } from 'ui/catalog/Properties';
import McpdDate from 'ui/common/time/McpdDate';
import Markdown from 'ui/common/markdown';
let Map;
let Marker;
......@@ -122,7 +123,9 @@ class LocationMap extends React.Component<ILocationMapProps, any> {
.join(', ')
}
</PropertiesItem>
<PropertiesItem title="Date of evaluation:"><McpdDate value={ location.startDate }/> &mdash; <McpdDate value={ location.endDate }/></PropertiesItem>
{ (location.startDate || location.endDate) && <PropertiesItem title="Date of evaluation:"><McpdDate value={ location.startDate }/> &mdash; <McpdDate value={ location.endDate }/></PropertiesItem> }
{ location.description && <PropertiesItem title="Description:"><Markdown source={ location.description }/></PropertiesItem> }
{ location.countryCode && <PropertiesItem title="ISO3 country code:">{ location.countryCode }</PropertiesItem> }
</Properties>
)) }
</Grid>
......
import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {Field} from 'redux-form';
// Model
import VocabularyTerm from 'model/vocabulary/VocabularyTerm';
// Service
import GeoService from 'service/GeoService';
// actions
import {autocompleteGeoTerm} from 'vocabulary/actions/dashboard';
// ui
import Markdown from 'ui/catalog/markdown';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Input from '@material-ui/core/Input';
import TextField from '@material-ui/core/TextField';
import FormControl from 'ui/common/forms/FormControl';
import Select from '@material-ui/core/Select';
interface ISelectCountryCodeInternal extends React.ClassAttributes<any> {
input: any;
listVocabularyTerms: VocabularyTerm[];
autocomplete: (term: string) => Promise<VocabularyTerm[]>;
label: string;
meta?: any;
}
class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal, any> {
protected handleInputChange = (event) => {
this.setState({searchText: event.target.value});
this.props.autocomplete(event.target.value).then((suggestions) => {
this.setState({suggestions});
});
}
protected handleClick = (event) => {
this.setState({open: true, anchorEl: event.currentTarget});
}
protected handleRequestClose = () => {
this.setState({open: false});
}
protected select = (value) => (event) => {
const {input} = this.props;
const countryCodeTerm = this.state.suggestions.find((dl) => dl.code === value);
if (countryCodeTerm) {
input.onChange(countryCodeTerm.code);
this.setState({
open: false,
pickerList: {code: countryCodeTerm.code, title: countryCodeTerm.title},
});
} else {
input.onChange('');
this.setState({
open: false,
pickerList: null,
});
}
}
private constructor(props, context) {
super(props, context);
this.state = {
pickerList: null,
open: false,
anchorEl: null,
searchText: '',
suggestions: [],
};
}
public componentWillMount() {
if (this.props.input.value) {
GeoService.get(this.props.input.value).then((dl) => {
this.setState({pickerList: {code: dl.code, title: dl.title}});
});
}
this.props.autocomplete('').then((data) => {
this.setState({suggestions: data});
});
}
public componentWillReceiveProps(nextProps) {
if (nextProps.input.value) {
const code = nextProps.input.value;
const countryCodeTerm = this.state.suggestions.find((dl) => dl.code === code);
if (countryCodeTerm) {
this.setState({pickerList: {code: countryCodeTerm.code, title: countryCodeTerm.title}});
} else {
GeoService.get(code).then((dl) => {
this.setState({pickerList: {code: dl.code, title: dl.title}});
});
}
} else {
this.setState({pickerList: null});
}
}
public render() {
const {label, meta} = this.props;
const inputValue = this.state.pickerList && this.state.pickerList.code ? this.state.pickerList.code : '';
return (
<FormControl fullWidth meta={ meta } label={ label }>
<Select
onClick={ this.handleClick }
value={ inputValue }
input={ <Input/> }
MenuProps={ {
open: false,
} }
>
{
this.state.pickerList ? (
<MenuItem value={ inputValue }>
<Markdown basic source={ this.state.pickerList.title }/>
</MenuItem>) : null
}
</Select>
<Menu
anchorEl={ this.state.anchorEl }
open={ this.state.open }
onClose={ this.handleRequestClose }
>
<MenuItem>
<TextField fullWidth
value={ this.state.searchText }
label="Search..."
onChange={ this.handleInputChange }
/>
</MenuItem>
{
this.state.suggestions.map((cCT, index) => (
<MenuItem onClick={ this.select(cCT.code) } key={ `country-iso3-${index}` }>
<Markdown basic source={ cCT.title }/>
</MenuItem>
))
}
</Menu>
</FormControl>
);
}
}
interface ICountryCodePicker extends React.ClassAttributes<any> {
input: any;
label: string;
className?: string;
autocomplete: (term: string) => Promise<VocabularyTerm[]>;
}
class CountryCodePicker extends React.Component<ICountryCodePicker, any> {
public render() {
const {input: {name}, label, className, autocomplete} = this.props;
return (
<div className={ className }>
<Field
name={ name }
component={ SelectCountryInternal }
label={ label }
autocomplete={ autocomplete }
/>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => bindActionCreators({
autocomplete: autocompleteGeoTerm,
}, dispatch);
export default connect(null, mapDispatchToProps)(CountryCodePicker);
......@@ -6,6 +6,7 @@ import { LOCATION_FORM } from 'datasets/constants';
import MaterialAutosuggest from 'ui/common/material-autosuggest';
import { TextField } from 'ui/common/text-field';
import MarkdownField from 'ui/common/markdown/MarkdownField';
import countries from 'data/countries';
import FormMap from './FormMap';
import Dataset from 'model/catalog/Dataset';
......@@ -17,6 +18,7 @@ import Validators from 'utilities/Validators';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
import GeoNamesService from 'service/GeoNamesService';
import CountryCodePicker from './CountryCodePicker';
interface ILocationFormProps extends React.ClassAttributes<any> {
......@@ -105,6 +107,8 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
`${location}.decimalLongitude`,
`${location}.startDate`,
`${location}.endDate`,
`${location}.description`,
`${location}.countryCode`,
] }
checkGeonames={ this.checkGeonames }
component={ FormMap }
......@@ -167,6 +171,18 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
placeholder="YYYYMMDD. If the month or day are missing, this should be indicated with ‘00’ [double zero] (e.g. 1975----, 19750000; 197506--, 19750600)"
validate={ [ Validators.mcpdDate ] }
/>
<Field
name={ `${location}.description` }
component={ MarkdownField }
label="Description of environment and conditions at the site of evaluation"
/>
<Field
name={ `${location}.countryCode` }
component={ CountryCodePicker }
label="ISO3 Country code"
placeholder="GBR"
validate={ [ Validators.maxLength(3) ] }
/>
</div>
)) }
<div className="back-white m-20">
......
import * as QueryString from 'query-string';
import * as UrlTemplate from 'url-template';
import {axiosBackend} from 'utilities/requestUtils';
import VocabularyTerm from 'model/vocabulary/VocabularyTerm';
const URL_AUTOCOMPLETE_GEO_TERM = `/api/v0/geo/iso3166/autocomplete`;
const URL_GET = UrlTemplate.parse(`/api/v0/geo/iso3166/{code}`);
/*
* Defined in Swagger as 'geo'
*/
class GeoService {
/**
* autocompleteGeoTerm at /api/v0/geo/iso3166/autocomplete
*
* @param c code
*/
public static autocompleteGeoTerm(c: string): Promise<VocabularyTerm[]> {
const qs = QueryString.stringify({
c: c || undefined,
}, {});
const apiUrl = URL_AUTOCOMPLETE_GEO_TERM + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = {/* No content in request body */};
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({data}) => data as VocabularyTerm[]);
}
/**
* get at /api/v0/geo/iso3166/{code}
*
* @param code code
*/
public static get(code?: string): Promise<any> {
const apiUrl = URL_GET.expand({code});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as any);
}
}
export default GeoService;
......@@ -2,6 +2,7 @@ import * as React from 'react';
import Input from '@material-ui/core/Input';
import FormControl from 'ui/common/forms/FormControl';
import Markdown from 'ui/common/markdown';
import FormHelperText from '@material-ui/core/FormHelperText';
class MarkdownField extends React.Component<any, any> {
......@@ -26,15 +27,15 @@ class MarkdownField extends React.Component<any, any> {
{ (basic || !this.state.previewMode) ?
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ touched && error } multiline={ !basic } { ...input } />
<h6>
<FormHelperText>
{ ! basic && <a onClick={ this.onChangePreviewMode }>Preview Markdown</a> }
<span> { basic ? 'Basic markdown supported: * **' : 'Full markdown supported' }</span>
</h6>
</FormHelperText>
</FormControl>
:
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Markdown style={ { marginTop: '2rem' } } basic={ basic } source={ input.value } />
<h6><a onClick={ this.onChangePreviewMode }>Edit Markdown</a></h6>
<FormHelperText><a onClick={ this.onChangePreviewMode }>Edit Markdown</a></FormHelperText>
</FormControl>
}
</div>
......
......@@ -123,7 +123,7 @@ class LangListComponent extends React.Component<ILangListComponentProps, any> {
{
// Using <a href="ru/" will reload app from server
languages.map((lang, index) => (
<a key={ (lang.short) } href={ `${lang.short}/` }><MenuItem>{ `${lang.label}` }</MenuItem></a>
<a key={ (lang.short) } href={ `${lang.short === 'en' ? '' : lang.short}/` }><MenuItem>{ `${lang.label}` }</MenuItem></a>
))
}
</Menu>
......
import GeoService from 'service/GeoService';
export const autocompleteGeoTerm = (term: string) => (dispatch, getState) => {
if (!term) {
return Promise.resolve([]);
}
return GeoService.autocompleteGeoTerm(term);
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment