Commit 75e0db40 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov
Browse files

Institute display page details

Added PropertiesCard

Added missing components
parent 74b4df22
......@@ -5,10 +5,10 @@ import FaoInstitute from 'model/FaoInstitute';
*/
class FaoInstituteDetails {
public details: FaoInstitute;
public blurb: string;
public blurb: any;
public pdciStats: any;
public lastUpdates: any;
public overview: any;
public overview: Map<string, any>;
}
export default FaoInstituteDetails;
......@@ -49,8 +49,8 @@ class PropertiesItem1 extends React.Component<IItemProps & WithStyles<'propertie
return (
<Grid item xs={ 12 } md={ small ? 3 : 12 } className={ classes.propertiesRow }>
<Grid container spacing={ 0 }>
<Grid item xs={ small ? 8 : 12 } md={ small ? 10 : 3 } className="font-bold p-halfrem" style={ { fontSize: '1rem', fontFamily: 'Roboto, sans-serif' } }>{ title }</Grid>
<Grid item xs={ small ? 4 : 12 } md={ small ? 2 : 9 } className={ `${classes.propertiesValue} p-halfrem` } style={ { fontSize: '1rem' } }>{ children }</Grid>
<Grid item xs={ small ? 8 : 6 } md={ small ? 10 : 3 } className="font-bold p-halfrem" style={ { fontSize: '1rem', fontFamily: 'Roboto, sans-serif' } }>{ title }</Grid>
<Grid item xs={ small ? 4 : 6 } md={ small ? 2 : 9 } className={ `${classes.propertiesValue} p-halfrem` } style={ { fontSize: '1rem' } }>{ children }</Grid>
</Grid>
</Grid>
);
......
import * as React from 'react';
import Grid from '@material-ui/core/Grid';
import {Properties, PropertiesItem} from 'ui/common/Properties';
import {PageSection} from 'ui/layout/PageLayout';
interface IBundledProps extends React.ClassAttributes<any> {
children?: any;
propertiesList: Array<{title: string, value: any}>;
title: string;
small?: boolean;
}
class PropertiesCard extends React.Component<IBundledProps, any> {
public render() {
const {children = null, propertiesList, title, small = false} = this.props;
return (
<Grid item lg={ small ? 6 : 12 } md={ 12 } xs={ 12 }>
<PageSection title={ title }>
<Grid container justify={ 'center' } spacing={ 16 }>
<Grid item md={ children ? 7 : 12 } sm={ 12 }>
<Properties>
{
propertiesList.map((property) => (<PropertiesItem title={ property.title }>{ property.value }</PropertiesItem>))
}
</Properties>
</Grid>
{ children &&
<Grid container justify={ 'center' } item md={ 5 }>
{ children }
</Grid>
}
</Grid>
</PageSection>
</Grid>
);
}
}
export default PropertiesCard;
import * as React from 'react';
import { withStyles } from '@material-ui/core/styles';
import Tooltip from '@material-ui/core/Tooltip';
const styles = (theme) => ({
/*tslint:disable*/
sliceWrapper: {
'&:hover': {
opacity: 0.4,
backgroundColor: '#000',
}
},
tooltipText:{
fontSize: 16,
},
});
interface ISliceProps extends React.ClassAttributes<any> {
radius: number;
holeRadius: number;
startAngle: number;
angle: number;
value: number;
label: string;
color: string;
strokeColor: string;
strokeWidth: number;
classes: any;
}
class Slice extends React.Component<ISliceProps, any> {
public constructor(props) {
super(props);
this.state = {
path: '',
};
}
private getAnglePoint = (startAngle, endAngle, radius, x, y) => {
const x1 = x + radius * Math.cos(Math.PI * startAngle / 180);
const y1 = y + radius * Math.sin(Math.PI * startAngle / 180);
const x2 = x + radius * Math.cos(Math.PI * endAngle / 180);
const y2 = y + radius * Math.sin(Math.PI * endAngle / 180);
return { x1, y1, x2, y2 };
}
public componentDidMount() {
this.draw();
}
private draw = () => {
const { radius, holeRadius, startAngle, angle } = this.props;
const path = [];
// Get angle points
const a = this.getAnglePoint(startAngle, startAngle + angle, radius, radius, radius);
const b = this.getAnglePoint(startAngle, startAngle + angle, radius - holeRadius, radius, radius);
path.push('M' + a.x1 + ',' + a.y1);
path.push('A' + radius + ',' + radius + ' 0 ' + (angle > 180 ? 1 : 0) + ',1 ' + a.x2 + ',' + a.y2);
path.push('L' + b.x2 + ',' + b.y2);
path.push('A' + (radius - holeRadius) + ',' + (radius - holeRadius) + ' 0 ' + (angle > 180 ? 1 : 0) + ',0 ' + b.x1 + ',' + b.y1);
path.push('Z');
this.setState({
path: path.join(' '),
});
}
public render() {
const { color, strokeColor, strokeWidth, label, value, classes } = this.props;
return (
<g overflow="hidden" className={classes.sliceWrapper}>
<Tooltip title={ <b className={classes.tooltipText}> { label } ({ value }) </b> } >
<path
d={ this.state.path }
fill={ color }
stroke={ strokeColor }
strokeWidth={ strokeWidth }
/>
</Tooltip>
</g>
);
}
}
export default withStyles(styles)(Slice);
import * as React from 'react';
import Slice from './Slice';
interface IPieProps extends React.ClassAttributes<any> {
radius?: number;
holeRadius?: number;
data: Array<{value: number, label: any}>;
strokeColor?: string;
strokeWidth?: string;
}
const colors = [ [237, 194, 64], [175, 216, 248], [203, 75, 75], [77, 167, 77], [148, 64, 237]];
class Pie extends React.Component<IPieProps, any> {
private getColor = (index) => {
const rgb = colors[index % colors.length];
const colorShift = (5 - index / colors.length) / 5;
const red = rgb[0] * colorShift;
const green = rgb[1] * colorShift;
const blue = rgb[2] * colorShift;
return 'rgb(' + red + ', ' + green + ', ' + blue + ')';
}
public render() {
const { radius = 100, holeRadius = 80, strokeColor = '#fff', strokeWidth = 1, data } = this.props;
const diameter = radius * 2;
const sum = data.reduce((accumulator, current) => accumulator + current.value, 0);
let startAngle = 270;
return (
<svg width={ diameter } height={ diameter } viewBox={ '0 0 ' + diameter + ' ' + diameter }>
{ data.map((sliceData, sliceIndex) => {
const nextAngle = startAngle;
const angle = (sliceData.value / sum) * 359.99;
startAngle += angle;
return (
<Slice
key={ sliceIndex }
value={ sliceData.value }
label={ sliceData.label }
startAngle={ nextAngle }
angle={ angle }
radius={ radius }
holeRadius={ radius - holeRadius }
color={ this.getColor(sliceIndex) }
strokeColor={ strokeColor }
strokeWidth={ data.length > 1 ? strokeWidth : 0 }
/>
);
}) }
</svg>
);
}
}
export default Pie;
import * as React from 'react';
import Grid from '@material-ui/core/Grid';
import {withStyles} from '@material-ui/core/styles';
const spacing = 16;
const totalMargin = 14;
const styles = (theme) => ({
root: {
width: 'auto' as 'auto',
margin: `${0 - spacing / 2}px ${totalMargin - spacing / 2}px`,
},
});
const Layout = ({children = null, classes}) => (
<Grid container spacing={ spacing } className={ classes.root }>
{ children }
</Grid>
);
export default withStyles(styles)(Layout);
......@@ -45,7 +45,7 @@ const styles = (theme) => ({
// color: 'Blue',
},
section: {
height: '100%',
},
sectionTitle: {
fontSize: '1.3rem',
......
......@@ -11,39 +11,62 @@ import FaoInstituteDetails from 'model/FaoInstituteDetails';
// UI
import PageLayout, {MainSection, PageContents, PageSection} from 'ui/layout/PageLayout';
import GridLayout from 'ui/layout/GridLayout';
import PropertiesCard from 'ui/common/PropertiesCard.tsx';
import ContentHeader from 'ui/common/heading/ContentHeader';
import Loading from 'ui/common/Loading';
import { ScrollToTopOnMount } from 'ui/common/page/scrollers';
import {Properties, PropertiesItem} from 'ui/common/Properties';
import LocationMap from './с/LocationMap';
import PieChart from 'ui/common/pie-chart';
import PrettyDate from 'ui/common/time/PrettyDate';
import withWidth from '@material-ui/core/withWidth';
import {Breakpoint} from '@material-ui/core/styles/createBreakpoints';
interface IBrowsePageProps extends React.ClassAttributes<any> {
t: any;
width: Breakpoint;
code: string;
doi: string; // DOI comes from the route without the '10.'
institute: FaoInstituteDetails;
error: any;
loadInstitute: any;
}
const mobile = ['sm', 'xs'] as Breakpoint[];
class BrowsePage extends React.Component<IBrowsePageProps, any> {
constructor(props: IBrowsePageProps, context: any) {
super(props, context);
super(props, context);
}
public componentWillMount() {
const { institute, code, loadInstitute } = this.props;
if (code && (! institute || code !== institute.details.code)) {
loadInstitute({ code });
loadInstitute({ code });
}
}
public render() {
const { error, institute, code } = this.props;
const stillLoading: boolean = ! error && (! institute
|| (code && institute && institute.details.code !== code));
const { error, institute, code, width } = this.props;
const stillLoading: boolean = ! error && (! institute || (code && institute && institute.details.code !== code));
const isMobile = mobile.indexOf(width) !== -1;
let cropShortNameOverview;
let cropNameOverview;
let taxonomyGenusOverview;
let taxonomyGenusSpeciesOverview;
if (!stillLoading) {
const cropShortNameKey = 'crop.shortName';
cropShortNameOverview = institute.overview[cropShortNameKey];
const cropNameKey = 'cropName';
cropNameOverview = institute.overview[cropNameKey];
const taxonomyGenusKey = 'taxonomy.genus';
taxonomyGenusOverview = institute.overview[taxonomyGenusKey];
const taxonomyGenusSpeciesKey = 'taxonomy.genusSpecies';
taxonomyGenusSpeciesOverview = institute.overview[taxonomyGenusSpeciesKey];
}
return (
<PageLayout>
......@@ -53,32 +76,117 @@ class BrowsePage extends React.Component<IBrowsePageProps, any> {
{ stillLoading && <Loading /> }
{ error && <div>{ JSON.stringify(error) }</div> }
{ institute &&
{ institute &&
<PageContents>
<MainSection title={ `${institute.details.fullName}` }>
<Properties>
<PropertiesItem title="Institute code">{ institute.details.code }</PropertiesItem>
<PropertiesItem title="Type">{ institute.details.type }</PropertiesItem>
<PropertiesItem title="Country">{ institute.details.country.name }</PropertiesItem>
{ institute.details.url &&
<PropertiesItem title="Web link">
<a href={ institute.details.url }> { institute.details.url }</a>
</PropertiesItem>
}
<PropertiesItem title="Accessions in Genesys">{ institute.details.accessionCount }</PropertiesItem>
</Properties>
</MainSection>
{ institute.details.latitude !== null && institute.details.longitude !== null && <PageSection title="Location">
<Properties>
<PropertiesItem title="latitude">{ institute.details.latitude }</PropertiesItem>
<PropertiesItem title="longitude">{ institute.details.longitude }</PropertiesItem>
</Properties>
<div className="p-20">
<LocationMap institute={ institute.details } classes={ {} }/>
</div>
</PageSection> }
<MainSection title={ `${institute.details.fullName}` }>
<Properties>
<PropertiesItem title="Institute code">{ institute.details.code }</PropertiesItem>
<PropertiesItem title="Type">{ institute.details.type }</PropertiesItem>
<PropertiesItem title="Country">{ institute.details.country.name }</PropertiesItem>
{ institute.details.url &&
<PropertiesItem title="Web link">
<a href={ institute.details.url }> { institute.details.url }</a>
</PropertiesItem>
}
<PropertiesItem title="Accessions in Genesys">{ institute.details.accessionCount }</PropertiesItem>
</Properties>
</MainSection>
{ institute.details.latitude !== null && institute.details.longitude !== null &&
<PageSection title="Location">
<Properties>
<PropertiesItem title="latitude">{ institute.details.latitude }</PropertiesItem>
<PropertiesItem title="longitude">{ institute.details.longitude }</PropertiesItem>
</Properties>
<div className="p-20">
<LocationMap institute={ institute.details } classes={ {} }/>
</div>
</PageSection>
}
<GridLayout>
{ cropShortNameOverview && cropShortNameOverview.terms && cropShortNameOverview.terms.length > 0 &&
<PropertiesCard
title="Most represented Crops"
propertiesList={ cropShortNameOverview.terms.map((term) => ({title: term.term, value: term.count})) }
small
/>
}
{ cropNameOverview && cropNameOverview.terms && cropNameOverview.terms.length > 0 &&
<PropertiesCard
title="Most represented Crop names"
propertiesList={ cropNameOverview.terms.slice(0, 5).map((term) => ({title: term.term, value: term.count})) }
small
>
<PieChart
data={
cropNameOverview.terms.map(
(term) => ({
value: term.count,
label: term.term,
}),
)
}
/>
</PropertiesCard>
}
{ taxonomyGenusOverview && taxonomyGenusOverview.terms && taxonomyGenusOverview.terms.length > 0 &&
<PropertiesCard
title="Most represented Genera"
propertiesList={ taxonomyGenusOverview.terms.slice(0, 5).map((term) => ({title: term.term, value: term.count})) }
small
>
<PieChart
data={
taxonomyGenusOverview.terms.map(
(term) => ({
value: term.count,
label: term.term,
}),
)
}
/>
</PropertiesCard>
}
{ taxonomyGenusSpeciesOverview && taxonomyGenusSpeciesOverview.terms && taxonomyGenusSpeciesOverview.terms.length > 0 &&
<PropertiesCard
title="Most represented Species"
propertiesList={ taxonomyGenusSpeciesOverview.terms.slice(0, 5).map((term) => ({title: term.term, value: term.count})) }
small
>
<PieChart
data={
taxonomyGenusSpeciesOverview.terms.map(
(term) => ({
value: term.count,
label: term.term,
}),
)
}
/>
</PropertiesCard>
}
{ institute.lastUpdates && institute.lastUpdates.length > 0 &&
<PropertiesCard
title="Last updates of passport data"
propertiesList={ institute.lastUpdates.map((lastUpdate) => ({title: PrettyDate({value: new Date(lastUpdate[0], lastUpdate[1], lastUpdate[2])}) , value: lastUpdate[3]})) }
small
/>
}
</GridLayout>
{ institute.blurb && institute.blurb.body &&
<PageSection title="About">
<Properties>
<div style={ !isMobile ? {columnCount: 2} : {} } dangerouslySetInnerHTML={ {__html: institute.blurb.body} }/>
</Properties>
</PageSection>
}
</PageContents>
}
}
</PageLayout>
);
}
......@@ -95,4 +203,4 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(translate()(BrowsePage));
export default connect(mapStateToProps, mapDispatchToProps)(withWidth()(translate()(BrowsePage)));
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