Commit da2e0d0d authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '92-geo-module' into 'master'

Resolve "Geo module"

Closes #92

See merge request genesys-pgr/genesys-ui!94
parents bf0da03d 264b3631
......@@ -87,7 +87,7 @@
"users": {
"role": "Role",
"enabled": "Active",
"expired": "Exptired",
"expired": "Expired",
"locked": "Locked",
"email": "E-mail address",
"uuid": "UUID"
......@@ -170,15 +170,6 @@
}
}
}
},
"geo": {
"common": {
"location": "Location",
"latitude": "Latitude",
"longitude": "Longitude",
"address": "Address",
"country": "Country"
}
}
,"accessions": {
"public": {
......@@ -412,6 +403,43 @@
"cropList": "List of crops"
}
}
,"geo": {
"public": {
"p": {
"countryList": {
"title": "ISO-3316 list",
"subTitle": "Find registered institutes as well as overview on plant genetic resources",
"acceCount": "{{count, number}} accessions",
"acceCountAtInstitutes": "{{count, number}} accessions at {{institutes, number}} institutes",
"accessionsInGenesys": "Provenance of accessions",
"institutesCountInWIEWS": "{{count, number}} institutes registered in WIEWS",
"instituteLocations": "Institute locations",
"moreInformation": "More information",
"isoCodes": "ISO-3316",
"countryProfile": "ISO-3316 code {{code}}"
},
"regionList": {
"title": "FAO Geographical regions",
"subTitle": "FAO Geographical regions lists below allow you to access the data about accessions collected or maintained in the region.",
"listOfSubRegions": "List of subregions",
"listOfCountries": "List of countries",
"b": {
"listAllFaoRegions": "List all FAO regions",
"showParentRegion": "Show parent region {{what, lowercase}}"
}
}
}
},
"common": {
"location": "Location",
"latitude": "Latitude",
"longitude": "Longitude",
"address": "Address",
"country": "Country",
"menu": "ISO-3316 codes"
}
}
,"institutes": {
"public": {
"c": {
......
......@@ -207,15 +207,7 @@ class BrowsePage extends React.Component<IBrowsePageProps, any> {
</Properties>
{ hasLatLon &&
<LocationMap
location={
{
id: accession.geo.id,
lat: accession.geo.latitude,
lng: accession.geo.longitude,
}
}
/>
<LocationMap locations={ [{ id: accession.geo.id, lat: accession.geo.latitude, lng: accession.geo.longitude }] }/>
}
</PageSection>
}
......
......@@ -41,7 +41,7 @@ class BrowsePage extends React.Component<IBrowsePageProps> {
<ContentHeaderWithButton buttons={ <ActionButton title={ t('crop.public.p.browse.createCrop') } action={ this.addNewCropHandle } style={ {margin: '4px', padding: '0'} }/> }/>
</Authorize>
<PageContents>
<GridLayout>
<GridLayout style={ { width: 'auto', margin: '8px 6px' } }>
{ crops && crops.sort((a, b) => a.name.localeCompare(b.name)).map((crop) => <CropCard key={ crop.shortName } crop={ crop } compact />) }
</GridLayout>
</PageContents>
......
import {log} from 'utilities/debug';
import GeoService from 'service/genesys/GeoService';
import {IReducerAction} from 'model/common.model';
import {RECEIVE_COUNTRIES, RECEIVE_COUNTRY_DETAILS, RECEIVE_REGION_DETAILS, RECEIVE_REGIONS} from 'geo/constants';
import CountryDetails from 'model/geo/CountryDetails';
import Country from 'model/geo/Country';
import GeoRegion from 'model/geo/GeoRegion';
import RegionDetails from 'model/geo/RegionDetails';
const receiveCountries = (countries: Country[]): IReducerAction => ({
type: RECEIVE_COUNTRIES, payload: countries,
});
const receiveCountryDetails = (countryDetails: CountryDetails): IReducerAction => ({
type: RECEIVE_COUNTRY_DETAILS, payload: countryDetails,
});
const receiveRegions = (regions: GeoRegion[]): IReducerAction => ({
type: RECEIVE_REGIONS, payload: regions,
});
const receiveRegionDetails = (regionDetails: RegionDetails): IReducerAction => ({
type: RECEIVE_REGION_DETAILS, payload: regionDetails,
});
export const loadCountries = () => (dispatch, getState) => {
return GeoService.listCountries()
.then((list: Country[]) => {
dispatch(receiveCountries(list));
})
.catch((error) => {
log('Error', error);
});
};
export const loadRegions = () => (dispatch, getState) => {
return GeoService.getGeoRegions()
.then((list: GeoRegion[]) => {
dispatch(receiveRegions(list));
})
.catch((error) => {
log('Error', error);
});
};
export const loadCountryDetails = (iso3code: string) => (dispatch, getState) => {
dispatch(receiveCountryDetails(null));
return GeoService.getCountryDetails(iso3code)
.then((details: CountryDetails) => {
dispatch(receiveCountryDetails(details));
})
.catch((error) => {
log('Error', error);
});
};
export const loadRegionDetails = (isoCode: string) => (dispatch, getState) => {
dispatch(receiveRegionDetails(null));
return GeoService.getGeoRegion(isoCode)
.then((details: RegionDetails) => {
dispatch(receiveRegionDetails(details));
})
.catch((error) => {
log('Error', error);
});
};
export const RECEIVE_COUNTRIES = 'App/Geo/RECEIVE_COUNTRIES';
export const RECEIVE_REGIONS = 'App/Geo/RECEIVE_REGIONS';
export const RECEIVE_COUNTRY_DETAILS = 'App/Geo/RECEIVE_COUNTRY_DETAILS';
export const RECEIVE_REGION_DETAILS = 'App/Geo/RECEIVE_REGION_DETAILS';
import { combineReducers } from 'redux';
import geoPublic from './public';
const rootReducer = combineReducers({
public: geoPublic,
});
export default rootReducer;
import update from 'immutability-helper';
import { IReducerAction } from 'model/common.model';
import {RECEIVE_COUNTRIES, RECEIVE_COUNTRY_DETAILS, RECEIVE_REGIONS, RECEIVE_REGION_DETAILS} from 'geo/constants';
const INITIAL_STATE = {
countryList: null,
countryDetails: null,
regionList: null,
regionDetails: null,
};
export default function geo(state = INITIAL_STATE, action: IReducerAction = {type: ''}) {
switch (action.type) {
case RECEIVE_COUNTRIES: {
return update(state, {
countryList: {$set: action.payload},
});
}
case RECEIVE_COUNTRY_DETAILS: {
return update(state, {
countryDetails: {$set: action.payload},
});
}
case RECEIVE_REGIONS: {
return update(state, {
regionList: {$set: action.payload},
});
}
case RECEIVE_REGION_DETAILS: {
return update(state, {
regionDetails: {$set: action.payload},
});
}
default:
return state;
}
}
// Root
import CountryListPage from 'geo/ui/CountryListPage';
import CountryDisplayPage from 'geo/ui/CountryDisplayPage';
import RegionListPage from 'geo/ui/RegionListPage';
import RegionDisplayPage from 'geo/ui/RegionDisplayPage';
const publicRoutes = [
// Root
{
path: '/geo/regions',
component: RegionListPage,
exact: true,
},
{
path: '/geo/region/:isoCode',
component: RegionDisplayPage,
exact: true,
},
{
path: '/iso3316/:isoCode',
component: CountryDisplayPage,
exact: true,
},
{
path: '/iso3316/',
component: CountryListPage,
exact: true,
},
// Obsolete
{
path: '/geo/:isoCode',
component: CountryDisplayPage,
exact: true,
},
{
path: '/geo/',
component: CountryListPage,
exact: true,
},
];
export {publicRoutes as geoPublicRoutes};
{
"public": {
"p": {
"countryList": {
"title": "ISO-3316 list",
"subTitle": "Find registered institutes as well as overview on plant genetic resources",
"acceCount": "{{count, number}} accessions",
"acceCountAtInstitutes": "{{count, number}} accessions at {{institutes, number}} institutes",
"accessionsInGenesys": "Provenance of accessions",
"institutesCountInWIEWS": "{{count, number}} institutes registered in WIEWS",
"instituteLocations": "Institute locations",
"moreInformation": "More information",
"isoCodes": "ISO-3316",
"countryProfile": "ISO-3316 code {{code}}"
},
"regionList": {
"title": "FAO Geographical regions",
"subTitle": "FAO Geographical regions lists below allow you to access the data about accessions collected or maintained in the region.",
"listOfSubRegions": "List of subregions",
"listOfCountries": "List of countries",
"b": {
"listAllFaoRegions": "List all FAO regions",
"showParentRegion": "Show parent region {{what, lowercase}}"
}
}
}
},
"common": {
"location": "Location",
"latitude": "Latitude",
"longitude": "Longitude",
"address": "Address",
"country": "Country",
"menu": "ISO-3316 codes"
}
}
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { translate } from 'react-i18next';
// Actions
import { loadCountryDetails } from 'geo/actions/public';
import { applyFilters } from 'accessions/actions/public';
// Model
import CountryDetails from 'model/geo/CountryDetails';
import FaoInstitute from 'model/genesys/FaoInstitute';
// UI
import PageLayout, { MainSection, PageContents, PageSection } from 'ui/layout/PageLayout';
import { Properties, PropertiesItem } from 'ui/common/Properties';
import { InstituteLink } from 'ui/genesys/Links';
import { ExternalLink } from 'ui/common/Links';
import ContentHeader from 'ui/common/heading/ContentHeader';
import LocationMap from 'ui/common/LocationMap';
import Loading from 'ui/common/Loading';
import Number from 'ui/common/Number';
interface ICountryDisplayPageProps extends React.ClassAttributes<any> {
details: CountryDetails;
isoCode: string;
t: any;
loadCountryDetails: (isoCode: string) => void;
applyFilters: any;
}
class CountryDisplayPage extends React.Component<ICountryDisplayPageProps> {
protected static needs = [
({ params: { isoCode } }) => {
return isoCode ? loadCountryDetails(isoCode) : null;
},
];
constructor(props) {
super(props);
}
public componentWillMount() {
const { loadCountryDetails, isoCode } = this.props;
if (isoCode) {
loadCountryDetails(isoCode);
}
}
public componentWillReceiveProps(nextProps) {
const { loadCountryDetails, details, isoCode } = nextProps;
if (isoCode && (! details || details.code3 !== isoCode)) {
loadCountryDetails(isoCode);
}
}
private getLocations = (institutes: FaoInstitute[]) => {
const instLocations = [];
institutes.forEach((inst) => {
if (inst.longitude != null && inst.longitude != null) {
instLocations.push({ id: inst.id, lat: inst.latitude, lng: inst.longitude });
}
});
return instLocations;
}
private applyCountryOfOriginFilter = (iso3Code: string) => {
const filter = {
origin: {iso3: [ iso3Code ]},
};
this.props.applyFilters(filter);
}
public render() {
const {details, isoCode, t} = this.props;
const stillLoading: boolean = (! details || details.code3 !== isoCode);
return (
<PageLayout>
<ContentHeader title={ `${t('geo.public.p.countryList.countryProfile', { code: isoCode }) }` }/>
<div>
{ stillLoading ? <Loading /> :
<PageContents>
<MainSection title={ `${details.name}` }>
<Properties>
<PropertiesItem title={ t('geo.public.p.countryList.moreInformation') }>
<ExternalLink link={ details.wikiLink }>{ details.wikiLink }</ExternalLink>
</PropertiesItem>
<PropertiesItem title={ `${t('geo.public.p.countryList.isoCodes')}` }>
<span>{ `ISO-3166 3-alpha: ${details.code3}, ISO-3166 2-alpha: ${details.code2}` }</span>
</PropertiesItem>
<PropertiesItem title={ t('geo.public.p.countryList.accessionsInGenesys') }>
<a onClick={ () => this.applyCountryOfOriginFilter(details.code3) }><Number value={ details.accessionCount || 0 } /></a>
</PropertiesItem>
</Properties>
</MainSection>
{ details.genesysInstitutes && details.genesysInstitutes.length > 0 &&
<PageSection title={ t('geo.public.p.countryList.instituteLocations') }>
<LocationMap locations={ this.getLocations(details.genesysInstitutes) } />
</PageSection>
}
{ details.genesysInstitutes && details.genesysInstitutes.length > 0 &&
<PageSection title={ `${t('geo.public.p.countryList.acceCountAtInstitutes', {count: details.countByLocation, institutes: details.genesysInstitutes.length }) }` }>
<Properties>
{ details.genesysInstitutes.map((institute, index) => (
<PropertiesItem key={ `${institute.id}-genesys` } numeric
title={
<span>
{ /* { `${index + 1}` } */ }
<InstituteLink to={ institute }>
{ `${institute.code}${institute.fullName}` }
</InstituteLink>
</span>
}>{ `${t('geo.public.p.countryList.acceCount', {count: institute.accessionCount}) }` }</PropertiesItem>
)) }
</Properties>
</PageSection>
}
{ details.faoInstitutes && details.faoInstitutes.length > 0 &&
<PageSection title={ `${t('geo.public.p.countryList.institutesCountInWIEWS', {count: details.faoInstitutes.length}) }` }>
<Properties>
{ details.faoInstitutes.map((fao, index) => (
<PropertiesItem key={ `${fao.id}-fao` } numeric keepEmpty
title={
<span>
{ /* `${index + 1}` */ }
<InstituteLink to={ fao }>
{ `${fao.code}${fao.fullName}` }
</InstituteLink>
</span>
} />
)) }
</Properties>
</PageSection>
}
</PageContents>
}
</div>
</PageLayout>
);
}
}
const mapStateToProps = (state, ownProps) => ({
details: state.geo.public.countryDetails || undefined,
isoCode: ownProps.match.params.isoCode,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
loadCountryDetails,
applyFilters,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(translate()(CountryDisplayPage));
import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import { translate } from 'react-i18next';
// Models
import Country from 'model/geo/Country';
// Actions
import {loadCountries} from 'geo/actions/public';
// UI
import PageLayout, { PageContents } from 'ui/layout/PageLayout';
import ContentHeader from 'ui/common/heading/ContentHeader';
import GridLayout from 'ui/layout/GridLayout';
import CountryCard from 'geo/ui/c/CountryCard';
import Loading from 'ui/common/Loading';
interface ICountryListPageProps extends React.ClassAttributes<any> {
t: any;
countries: Country[];
loadCountries: () => void;
}
class CountryListPage extends React.Component<ICountryListPageProps> {
public static needs = [
() => loadCountries(),
];
constructor(props) {
super(props);
}
public componentWillMount() {
const { loadCountries, countries } = this.props;
if (!countries) {
loadCountries();
}
}
public render() {
const {countries, t} = this.props;
const stillLoading: boolean = (! countries);
return (
<PageLayout>
<ContentHeader
title={ t(`geo.public.p.countryList.title`) }
subTitle={ t(`geo.public.p.countryList.subTitle`) }
/>
<PageContents>
{ stillLoading ? <Loading /> :
<GridLayout style={ { width: 'auto', margin: '8px 6px' } }>
{
countries && countries.sort((a, b) => a.name.localeCompare(b.name)).map((country: Country) => (
<CountryCard key={ country.name } country={ country }/>
))
}
</GridLayout>
}
</PageContents>
</PageLayout>
);
}
}
const mapStateToProps = (state, ownProps) => ({
countries: state.geo.public.countryList || undefined,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
loadCountries,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(translate()(CountryListPage));
import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import { translate } from 'react-i18next';
// Models
import GeoRegion from 'model/geo/GeoRegion';
import Country from 'model/geo/Country';
import RegionDetails from 'model/geo/RegionDetails';
// Actions
import {loadRegionDetails} from 'geo/actions/public';
import navigateTo from 'actions/navigation';
// UI
import PageLayout, { PageContents } from 'ui/layout/PageLayout';
import ContentHeader from 'ui/common/heading/ContentHeader';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import GridLayout from 'ui/layout/GridLayout';
import Loading from 'ui/common/Loading';
import GeoRegionCard from 'geo/ui/c/GeoRegionCard';
import CountryCard from 'geo/ui/c/CountryCard';
import ActionButton from 'ui/common/buttons/ActionButton';
interface IRegionDisplayPageProps extends React.ClassAttributes<any> {
details: RegionDetails;
isoCode: string;
t: any;
i18n: any;
loadRegionDetails: (isoCode: string) => void;
navigateTo: (path: string) => void;
}
class RegionDisplayPage extends React.Component<IRegionDisplayPageProps> {
protected static needs = [
({ params: { isoCode } }) => {
return isoCode ? loadRegionDetails(isoCode) : null;
},
];
constructor(props) {
super(props);
}
public componentWillMount() {
const { loadRegionDetails, isoCode} = this.props;
if (isoCode) {
loadRegionDetails(isoCode);
}
}