Commit 90d5f068 authored by Matija Obreza's avatar Matija Obreza

Merge branch '674-add-pgrfa-network' into 'master'

Resolve "Add PGRFA Network"

Closes #674

See merge request genesys-pgr/genesys-ui!658
parents 4a88dfd9 631f280d
// actions
// constants
import {
REMOVE_NETWORK,
} from 'networks/constants';
// service
import NetworkService from 'service/genesys/NetworkService';
import PGRFANetwork from 'model/network/PGRFANetwork';
export const deleteNetwork = (network: PGRFANetwork) => (dispatch, getState) => {
return NetworkService.deleteNetwork(network.slug, network.version);
return NetworkService.deleteNetwork(network.slug, network.version)
.then(() => dispatch({type: REMOVE_NETWORK, payload: {slug: network.slug}}));
};
......@@ -4,6 +4,7 @@ import {resetPublicDetails, resetPublicInstitutes} from 'networks/actions/public
// Constants
import {
APPEND_NETWORK,
DASHBOARD_RECEIVE_NETWORK,
DASHBOARD_RECEIVE_NETWORK_BLURB,
DASHBOARD_RECEIVE_NETWORK_INSTITUTES,
......@@ -18,6 +19,7 @@ import NetworkService from 'service/genesys/NetworkService';
// Util
import { log } from 'utilities/debug';
import navigateTo from 'actions/navigation';
// Wrapped API Calls
......@@ -30,6 +32,19 @@ const apiSaveNetworkBlurb = createApiCaller(NetworkService.updateBlurb, DASHBOAR
const apiAddNetworkInstitutes = createPureApiCaller(NetworkService.addNetworkInstitutes);
const apiSetNetworkInstitutes = createPureApiCaller(NetworkService.setNetworkInstitutes);
export const createNetwork = ({institutes, blurb, ...network}: PGRFANetwork & {institutes: string[], blurb: string}) => (dispatch, getState) => {
dispatch(apiUpdateNetwork(network))
.then((newNetwork) => {
if (institutes && institutes.length > 0) {
dispatch(addNetworkInstitutes(newNetwork.slug, institutes));
}
dispatch(saveNetworkBlurb(network.slug, blurb || ''));
dispatch({type: APPEND_NETWORK, payload: {network: newNetwork}});
dispatch(navigateTo(`/admin/network/${newNetwork.slug}/edit`));
});
};
export const saveNetwork = ({institutes, blurb, ...network}: PGRFANetwork & {institutes: string[], blurb: string}) => (dispatch, getState) => {
const oldNetworkInstCodes = getState().networks.admin.networkInstitutes && getState().networks.admin.networkInstitutes.data;
......@@ -65,7 +80,7 @@ export const addNetworkInstitutes = (slug: string, instCodes: string[]) => (disp
.then((network) => {
const oldCodes = getState().networks.admin.networkInstitutes.data;
dispatch({type: DASHBOARD_RECEIVE_NETWORK_INSTITUTES, payload: {apiCall: ApiCall.success([...oldCodes, ...instCodes])}});
dispatch((apiListNetworkInstitutes(slug, '0')))
dispatch((apiListNetworkInstitutes(slug, {page: '0'})))
.then((institutes) => {
dispatch(resetPublicInstitutes(institutes));
});
......@@ -83,7 +98,7 @@ export const removeNetworkInstitutes = (slug: string, instCodes: string[]) => (d
return dispatch(apiSetNetworkInstitutes(slug, instToSet))
.then((network) => {
dispatch({type: DASHBOARD_RECEIVE_NETWORK_INSTITUTES, payload: {apiCall: ApiCall.success(instToSet)}});
dispatch(apiListNetworkInstitutes(slug, '0'))
dispatch(apiListNetworkInstitutes(slug, {page: '0'}))
.then((institutes) => {
dispatch(resetPublicInstitutes(institutes));
});
......
......@@ -6,6 +6,7 @@ import ApiCall from 'model/ApiCall';
import {APPEND_NETWORK_PAGE, RECEIVE_NETWORK_DETAILS, RECEIVE_NETWORK_INSTITUTES, RESET_NETWORK_DETAILS, RESET_NETWORK_INSTITUTES} from 'networks/constants';
// service
import NetworkService from 'service/genesys/NetworkService';
import {IPageRequest} from 'model/Page';
// Wrapped API Calls
......@@ -25,6 +26,6 @@ export const loadNetwork = (shortName: string, lang) => (dispatch) => {
return dispatch(apiLoadNetworkDetails(shortName, lang));
};
export const loadMoreNetworkInstitutes = (shortName: string, page: number = 1) => (dispatch) => {
return dispatch(apiLoadMoreNetworkInstitutes(shortName, page));
export const loadMoreNetworkInstitutes = (shortName: string, pageRequest: IPageRequest = {page: 0, size: 50}) => (dispatch) => {
return dispatch(apiLoadMoreNetworkInstitutes(shortName, pageRequest));
};
......@@ -4,6 +4,8 @@ export const RECEIVE_NETWORK_DETAILS = 'networks/RECEIVE_NETWORK_DETAILS';
export const RECEIVE_NETWORK_INSTITUTES = 'networks/RECEIVE_NETWORK_INSTITUTES';
export const RESET_NETWORK_DETAILS = 'networks/RESET_NETWORK_DETAILS';
export const RESET_NETWORK_INSTITUTES = 'networks/RESET_NETWORK_INSTITUTES';
export const APPEND_NETWORK = 'networks/APPEND_NETWORK';
export const REMOVE_NETWORK = 'networks/REMOVE_NETWORK';
export const DASHBOARD_RECEIVE_NETWORK = 'networks/dashboard/RECEIVE_NETWORK';
......
......@@ -7,7 +7,7 @@ import PGRFANetwork from 'model/network/PGRFANetwork';
import PGRFANetworkDetails from 'model/network/PGRFANetworkDetails';
import FaoInstitute from 'model/genesys/FaoInstitute';
// constants
import {APPEND_NETWORK_PAGE, RECEIVE_NETWORK_DETAILS, RECEIVE_NETWORK_INSTITUTES, RESET_NETWORK_DETAILS, RESET_NETWORK_INSTITUTES} from 'networks/constants';
import {APPEND_NETWORK, APPEND_NETWORK_PAGE, RECEIVE_NETWORK_DETAILS, RECEIVE_NETWORK_INSTITUTES, REMOVE_NETWORK, RESET_NETWORK_DETAILS, RESET_NETWORK_INSTITUTES} from 'networks/constants';
// utilities
import {dereferenceReferences2} from 'utilities';
......@@ -95,6 +95,38 @@ export default (state = INITIAL_STATE, action: IReducerAction = { type: '' }) =>
});
}
case APPEND_NETWORK: {
const {network} = action.payload;
if (!state.networks || !state.networks.data || state.networks.data.length === 0) {
return state;
}
const networks = [...state.networks.data, network];
return update(state, {
networks: {
data: {$set: networks},
},
});
}
case REMOVE_NETWORK: {
const {slug} = action.payload;
if (!state.networks || !state.networks.data || state.networks.data.length === 0) {
return state;
}
const networks = state.networks.data.filter((netw) => netw.slug !== slug);
return update(state, {
networks: {
data: {$set: networks},
},
});
}
default:
return state;
......
......@@ -4,6 +4,7 @@ import { bindActionCreators } from 'redux';
import { WithTranslation, withTranslation } from 'react-i18next';
// actions
import {listNetworks} from 'networks/actions/public';
import navigateTo from 'actions/navigation';
// model
import ApiCall from 'model/ApiCall';
import PGRFANetwork from 'model/network/PGRFANetwork';
......@@ -15,11 +16,14 @@ import Loading from 'ui/common/Loading';
import NetworkCard from 'networks/ui/c/NetworkCard';
import ErrorMessage from 'ui/common/error/ErrorMessage';
import { ScrollToTopOnMount } from 'ui/common/page/scrollers';
import Authorize from 'ui/common/authorized/Authorize';
import CreateNewButton from 'ui/common/buttons/CreateNewButton';
interface IBrowsePageProps extends React.ClassAttributes<any>, WithTranslation {
apiCall: ApiCall<PGRFANetwork[]>;
listNetworks: () => void;
navigateTo: (loc: string) => void;
t: any;
}
......@@ -32,6 +36,10 @@ class BrowsePage extends React.Component<IBrowsePageProps> {
listNetworks();
}
}
protected addNewNetwork = () => {
const {navigateTo} = this.props;
navigateTo('/admin/network/edit');
}
public render() {
......@@ -57,6 +65,12 @@ class BrowsePage extends React.Component<IBrowsePageProps> {
</div>
}
</PageContents>
<Authorize role="ROLE_ADMINISTRATOR">
<CreateNewButton
title="crop.common.modelName"
action={ this.addNewNetwork }
/>
</Authorize>
</PageLayout>
);
}
......@@ -69,6 +83,7 @@ const mapStateToProps = (state, ownProps) => ({
const mapDispatchToProps = (dispatch) => bindActionCreators({
listNetworks,
navigateTo,
}, dispatch);
export default (connect(mapStateToProps, mapDispatchToProps)(withTranslation()(BrowsePage)));
......
......@@ -41,7 +41,7 @@ interface IDisplayPageProps extends React.ClassAttributes<any> {
mapLayers: MapLayer[];
shortName: string;
loadNetwork: (shortName: string, lang: string) => void;
loadMoreNetworkInstitutes: (shortName: string, page: number) => void;
loadMoreNetworkInstitutes: (shortName: string, pageRequest: IPageRequest) => void;
applyFilters: (filters: string | AccessionFilter, page?: IPageRequest) => any;
applyOverviewFilters: (filters: string | AccessionFilter) => any;
deleteNetwork: (network: PGRFANetwork) => any;
......@@ -75,7 +75,7 @@ class DisplayPage extends React.Component<IDisplayPageProps> {
private loadMoreNetworkMembers = (page: Page<FaoInstitute>) => {
const {shortName, loadMoreNetworkInstitutes} = this.props;
return loadMoreNetworkInstitutes(shortName, page.number + 1);
return loadMoreNetworkInstitutes(shortName, {page: page.number + 1});
}
private renderInstitute = (s: FaoInstitute, index: number) => {
......
......@@ -5,7 +5,7 @@ import {WithTranslation, withTranslation} from 'react-i18next';
import navigateTo from 'actions/navigation';
import {deleteNetwork} from 'networks/actions/admin';
import {addNetworkInstitutes, loadNetwork, loadNetworkBlurb, loadNetworkInstitutes, removeNetworkInstitute, removeNetworkInstitutes, saveNetwork} from 'networks/actions/editor';
import {addNetworkInstitutes, createNetwork, loadNetwork, loadNetworkBlurb, loadNetworkInstitutes, removeNetworkInstitute, removeNetworkInstitutes, saveNetwork} from 'networks/actions/editor';
import ApiCall from 'model/ApiCall';
import PGRFANetwork from 'model/network/PGRFANetwork';
......@@ -32,6 +32,7 @@ interface INetworkEditPageProps extends React.ClassAttributes<any>, WithTranslat
loadNetworkBlurb: (slug: string) => void;
saveNetwork: (networkDetails: PGRFANetwork) => Promise<PGRFANetwork>;
createNetwork: (networkDetails: PGRFANetwork) => void;
addNetworkInstitutes: any;
removeNetworkInstitute: any;
......@@ -53,7 +54,11 @@ class NetworkEditPage extends React.Component<INetworkEditPageProps, any> {
({params: {slug}}) => slug && loadNetworkBlurb(slug),
];
public onSave = (saved) => {
const {saveNetwork, network, networkInstitutes, networkBlurb, showSnackbar, t} = this.props;
const {saveNetwork, createNetwork, network, networkInstitutes, networkBlurb, showSnackbar, slug, t} = this.props;
if (!slug) {
return createNetwork(saved);
}
const fullNetwork = {...network.data, institutes: networkInstitutes.data, blurb: networkBlurb && networkBlurb.data && networkBlurb.data.body};
if (!_.isEqual(fullNetwork, saved)) {
......@@ -77,7 +82,7 @@ class NetworkEditPage extends React.Component<INetworkEditPageProps, any> {
public componentDidMount() {
const {slug, network: networkCall, loadNetwork, networkInstitutes: networkInstitutesCall, loadNetworkInstitutes, networkBlurb: networkBlurbCall, loadNetworkBlurb} = this.props;
if (networkCall && networkCall.data && slug !== networkCall.data.slug) {
if (slug && networkCall && networkCall.data && slug !== networkCall.data.slug) {
loadNetwork(slug);
loadNetworkInstitutes(slug);
return loadNetworkBlurb(slug);
......@@ -98,7 +103,7 @@ class NetworkEditPage extends React.Component<INetworkEditPageProps, any> {
public componentDidUpdate(prevProps: Readonly<INetworkEditPageProps>) {
const {slug, network: networkCall, loadNetwork, networkInstitutes: networkInstitutesCall, loadNetworkInstitutes, networkBlurb: networkBlurbCall, loadNetworkBlurb} = this.props;
if (networkCall && networkCall.data && slug !== networkCall.data.slug) {
if (slug && networkCall && networkCall.data && slug !== networkCall.data.slug) {
loadNetwork(slug);
loadNetworkInstitutes(slug);
return loadNetworkBlurb(slug);
......@@ -107,7 +112,7 @@ class NetworkEditPage extends React.Component<INetworkEditPageProps, any> {
if (slug && (!networkCall || (!networkCall.data && !networkCall.loading))) {
loadNetwork(slug);
}
if (slug && (!networkInstitutesCall || (!networkInstitutesCall.data && !networkInstitutesCall.loading))) { // TODO
if (slug && (!networkInstitutesCall || (!networkInstitutesCall.data && !networkInstitutesCall.loading))) {
loadNetworkInstitutes(slug);
}
......@@ -117,14 +122,21 @@ class NetworkEditPage extends React.Component<INetworkEditPageProps, any> {
}
public render() {
const {t, network: networkCall, networkInstitutes: networkInstitutesCall, networkBlurb: networkBlurbCall, removeNetworkInstitute} = this.props;
const {data: network, loading} = networkCall || {data: null, loading: true};
const {data: networkInstitutes} = networkInstitutesCall || {data: null, loading: true};
const {data: networkBlurb} = networkBlurbCall || {data: null, loading: true};
const {t, network: networkCall, networkInstitutes: networkInstitutesCall, networkBlurb: networkBlurbCall, removeNetworkInstitute, slug} = this.props;
let {data: network, loading} = slug && networkCall || {data: null, loading: true};
let {data: networkInstitutes} = slug && networkInstitutesCall || {data: null, loading: true};
let {data: networkBlurb} = slug && networkBlurbCall || {data: null, loading: true};
const instCodes = networkInstitutes || [];
const totalInstitutes = networkInstitutes && networkInstitutes.length;
if (!slug) {
network = new PGRFANetwork();
loading = false;
networkInstitutes = [];
networkBlurb = new Article();
}
return (
<Grid item xs={12}>
<ScrollToTopOnMount/>
......@@ -166,6 +178,7 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
addNetworkInstitutes,
removeNetworkInstitutes,
saveNetwork,
createNetwork,
deleteNetwork,
showSnackbar,
}, dispatch);
......
import * as React from 'react';
import { connect } from 'react-redux';
import { NETWORK_FORM } from 'networks/constants';
import { Link } from 'react-router-dom';
import { log } from 'utilities/debug';
import Validators from 'utilities/Validators';
......@@ -101,7 +102,10 @@ class NetworkForm extends React.Component<any, void> {
<div>{ error && <strong>{ error }</strong> }</div>
<Button variant="contained" type="submit" disabled={ submitting || invalid }>{ t('common:action.saveChanges') }</Button>
<NetworkLink to={ initialValues }><Button className="ml-1rem">{ t('common:action.back') }</Button></NetworkLink>
{ initialValues.slug
? <NetworkLink to={ initialValues }><Button className="ml-1rem">{ t('common:action.back') }</Button></NetworkLink>
: <Link to="/network"><Button className="ml-1rem">{ t('common:action.back') }</Button></Link>
}
</form>
);
}
......
import update from 'immutability-helper';
import { IReducerAction } from 'model/common.model';
import Page from 'model/Page';
import FilteredPage from 'model/FilteredPage';
import FaoInstitute from 'model/genesys/FaoInstitute';
......@@ -28,20 +29,21 @@ export default (state = INITIAL_STATE, action: IReducerAction = {type: ''}) => {
});
}
case RECEIVE_PARTNER_INSTITUTES: {
const {apiCall: {loading, error, timestamp, data}} = action.payload;
const {apiCall} = action.payload;
if (!state.partnerInstitutes || !state.partnerInstitutes.data) {
return update(state, {
partnerInstitutes: {$set: apiCall},
});
}
if (data) {
dereferenceReferences2(data.content, FaoInstitute.DEREFERENCES);
if (apiCall.data && apiCall.data.content && apiCall.data.content.length > 0) {
dereferenceReferences2(apiCall.data.content, FaoInstitute.DEREFERENCES);
}
return update(state, {
partnerInstitutes: {
$set: {
loading,
error,
timestamp,
data: FilteredPage.merge(state.partnerInstitutes && state.partnerInstitutes.data, data),
},
data: { $set: Page.merge(state.partnerInstitutes && state.partnerInstitutes.data, apiCall.data) },
},
});
}
......
......@@ -7,7 +7,7 @@ import Article from 'model/cms/Article';
import FaoInstitute from 'model/genesys/FaoInstitute';
import PGRFANetwork from 'model/network/PGRFANetwork';
import PGRFANetworkDetails from 'model/network/PGRFANetworkDetails';
import Page from 'model/Page';
import Page, {IPageRequest} from 'model/Page';
const URL_LIST_NETWORKS = `/api/v1/network`;
const URL_UPDATE_NETWORK = `/api/v1/network`;
......@@ -179,13 +179,16 @@ class NetworkService {
* getNetworkInstitutes at /api/v1/network/{shortName}/institutes
*
* @param shortName shortName
* @param page page
* @param page undefined
* @param xhrConfig additional xhr config
*/
public static getNetworkInstitutes(shortName: string, page: number, xhrConfig?: any): Promise<Page<FaoInstitute>> {
public static getNetworkInstitutes(shortName: string, page?: IPageRequest, xhrConfig?: any): Promise<Page<FaoInstitute>> {
const qs = QueryString.stringify({
page: page || undefined,
p: page.page || undefined,
l: page.size || undefined,
d: page.direction ? page.direction : undefined,
s: page.properties || undefined,
}, {});
const apiUrl = URL_GET_NETWORK_INSTITUTES.expand({ shortName }) + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
......
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