Commit 2787d9ea authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '124-institute-edit' into 'master'

Institute edit

Closes #124

See merge request genesys-pgr/genesys-ui!123
parents 5ce2d195 9fa5ff78
......@@ -481,6 +481,20 @@
}
}
},
"dashboard": {
"c": {
"form": {
"gaTracker": "GA Tracker code",
"mailto": "Email address for material request",
"codeSGSV": "Code SGSV",
"uniqueAcceNumbs": "Unique accession numbers",
"uniqueAcceNumbsTrue": "Each accession number is unique within this institute",
"uniqueAcceNumbsFalse": "The same accession number may be used in separate collections in this institute",
"materialRequests": "Material requests",
"allowMaterialRequests": "Allow material requests"
}
}
},
"sort": {
"instituteCode": "Institute code"
},
......@@ -492,7 +506,8 @@
"stats_plural": "Institutes",
"instCode": "INSTCODE",
"instituteCode": "Institute code",
"accessionsInGenesys": "Accessions in Genesys"
"accessionsInGenesys": "Accessions in Genesys",
"instDetails": "{{instCode,string}} details"
}
}
,"kpi": {
......
// actions
import navigateTo from 'actions/navigation';
// Constants
import {DASHBOARD_APPEND_INSTITUTES, DASHBOARD_RECEIVE_INSTITUTE, DASHBOARD_RECEIVE_INSTITUTES} from 'institutes/constants';
// Model
import FilteredPage, {IPageRequest} from 'model/FilteredPage';
import FaoInstitute from 'model/genesys/FaoInstitute';
// service
import InstituteService from 'service/genesys/InstituteService';
import InstituteDetails from 'model/genesys/InstituteDetails';
const receiveInstitutes = (paged: FilteredPage<FaoInstitute>, error = null) => ({
type: DASHBOARD_RECEIVE_INSTITUTES,
......@@ -18,7 +18,7 @@ const appendInstitutes = (paged: FilteredPage<FaoInstitute>, error = null) => ({
payload: { paged, error },
});
const receiveInstitute = (institute: InstituteDetails, error = null) => ({
export const receiveInstitute = (institute: FaoInstitute, error = null) => ({
type: DASHBOARD_RECEIVE_INSTITUTE,
payload: { institute, error },
});
......@@ -39,7 +39,7 @@ export const loadInstitutesPage = (page: IPageRequest) => (dispatch, getState) =
};
export const loadInstitute = (code: string) => (dispatch) => {
return InstituteService.details(code)
return InstituteService.get(code)
.then((institute) => {
dispatch(receiveInstitute(institute));
}).catch((error) => {
......@@ -47,3 +47,11 @@ export const loadInstitute = (code: string) => (dispatch) => {
dispatch(receiveInstitute(null, error));
});
};
export const updateInstitute = (institute: FaoInstitute) => (dispatch) => {
return InstituteService.update(institute.code, institute)
.then((inst) => {
dispatch(receiveInstitute(inst));
return dispatch(navigateTo(`/wiews/${inst.code}`));
});
};
......@@ -7,3 +7,4 @@ export const DASHBOARD_APPEND_INSTITUTES = 'institutes/dashboard/APPEND_INSTITUT
export const DASHBOARD_RECEIVE_INSTITUTE = 'institutes/dashboard/RECEIVE_INSTITUTE';
export const INSTITUTE_FILTERFORM = 'Form/institutes/INSTITUTE_FILTERFORM';
export const INSTITUTE_FORM = 'Form/institutes/dashboard/INSTITUTE_FORM';
import InstituteBrowsePage from 'institutes/ui/BrowsePage';
import InstituteDisplayPage from 'institutes/ui/DisplayPage';
import InstituteEditPage from 'institutes/ui/dashboard/EditPage';
const publicRoutes = [
......@@ -16,7 +17,11 @@ const publicRoutes = [
];
const dashboardRoutes = [
// not yet
{
path: '/wiews/:wiewsCode([a-zA-Z]+[0-9]+)/edit',
component: InstituteEditPage,
exact: true,
},
];
export {publicRoutes as institutePublicRoutes, dashboardRoutes as instituteDashboardRoutes};
......
......@@ -30,6 +30,20 @@
}
}
},
"dashboard": {
"c": {
"form": {
"gaTracker": "GA Tracker code",
"mailto": "Email address for material request",
"codeSGSV": "Code SGSV",
"uniqueAcceNumbs": "Unique accession numbers",
"uniqueAcceNumbsTrue": "Each accession number is unique within this institute",
"uniqueAcceNumbsFalse": "The same accession number may be used in separate collections in this institute",
"materialRequests": "Material requests",
"allowMaterialRequests": "Allow material requests"
}
}
},
"sort": {
"instituteCode": "Institute code"
},
......@@ -41,6 +55,7 @@
"stats_plural": "Institutes",
"instCode": "INSTCODE",
"instituteCode": "Institute code",
"accessionsInGenesys": "Accessions in Genesys"
"accessionsInGenesys": "Accessions in Genesys",
"instDetails": "{{instCode,string}} details"
}
}
\ No newline at end of file
......@@ -27,6 +27,10 @@ import withWidth from '@material-ui/core/withWidth';
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
import { CountryLink } from 'ui/genesys/Links';
import BarChart from 'ui/common/bar-chart';
import { CardActions } from 'ui/common/Card';
import Button from '@material-ui/core/Button';
import Permissions from 'ui/common/permission/Permissions';
import FaoInstitute from 'model/genesys/FaoInstitute';
interface IDisplayPageProps extends React.ClassAttributes<any> {
t: any;
......@@ -127,6 +131,15 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
<a onClick={ this.applyInstituteCodeFilter }><Number value={ institute.details.accessionCount } /></a>
</PropertiesItem>
</Properties>
{ institute.details._permissions.manage &&
<CardActions className="container-spacing-vertical mt-15">
<Link to={ `/dashboard/wiews/${institute.details.code}/edit` }>
<Button variant="contained">{ t('common:action.edit') }</Button>
</Link>
<Permissions clazz={ FaoInstitute.clazz } id={ institute.details.id }/>
</CardActions>
}
</MainSection>
{ institute.blurb && institute.blurb.body &&
......
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { translate } from 'react-i18next';
// actions
import { loadInstitute, updateInstitute } from 'institutes/actions/dashboard';
// model
import FaoInstitute from 'model/genesys/FaoInstitute';
// utilities
import { log } from 'utilities/debug';
// ui
import { PageContents } from 'ui/layout/PageLayout';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import InstituteForm from './c/InstituteForm';
interface IInstituteEditPageProps extends React.ClassAttributes<any> {
t: any;
classes: any;
wiewsCode?: string;
institute: FaoInstitute;
loadInstitute: (code: string) => void;
updateInstitute: (inst: FaoInstitute) => void;
}
class InstituteEditPage extends React.Component<IInstituteEditPageProps, any> {
protected static needs = [
({ params: { wiewsCode } }) => loadInstitute(wiewsCode),
];
private onSave = (institute: any) => {
const { updateInstitute, wiewsCode } = this.props;
Object.getOwnPropertyNames(institute.settings)
.map((settingName) => {
institute.settings[settingName] = {
setting: settingName,
instCode: wiewsCode,
...institute.settings[settingName],
};
});
updateInstitute({ ...institute, code: wiewsCode });
}
public componentWillMount() {
const { institute, loadInstitute, wiewsCode } = this.props;
if (wiewsCode && (!institute || institute.code !== wiewsCode)) {
loadInstitute(wiewsCode);
}
}
public render() {
const { wiewsCode, t } = this.props;
let { institute } = this.props;
if (!institute && !wiewsCode) {
institute = new FaoInstitute();
}
if (!institute) {
log('No institute.');
return null;
}
return (
<PageContents className="pt-1rem">
<Grid item xs={ 12 }>
<Paper className="p-20 mb-10">
<InstituteForm
wiewsCode={ wiewsCode }
initialValues={ {
settings: institute.settings,
codeSGSV: institute.codeSGSV,
uniqueAcceNumbs: `${institute.uniqueAcceNumbs}`,
allowMaterialRequests: `${institute.allowMaterialRequests}`,
} }
onSubmit={ this.onSave }
t={ t }
/>
</Paper>
</Grid>
</PageContents>
);
}
}
const mapStateToProps = (state, ownProps) => ({
wiewsCode: ownProps.match.params.wiewsCode,
institute: state.institutes.dashboard.institute,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
loadInstitute,
updateInstitute,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(translate()(InstituteEditPage));
import * as React from 'react';
import { Link } from 'react-router-dom';
import { Field, reduxForm } from 'redux-form';
import Button from '@material-ui/core/Button';
import { INSTITUTE_FORM } from 'institutes/constants';
import Validators from 'utilities/Validators';
import Divider from '@material-ui/core/Divider';
import FormLabel from '@material-ui/core/FormLabel/FormLabel';
import { TextField } from 'ui/common/text-field';
import ReduxCheckbox from 'ui/common/checkbox';
import FormControl from 'ui/common/forms/FormControl';
import BooleanRadioGroup from 'requests/ui/request-stepper/steps/requestInfo/c/BooleanRadioGroup';
const InstituteForm = ({ error, handleSubmit, initialValues, wiewsCode, t }) => {
return (
<form onSubmit={ handleSubmit }>
<Field
required
name={ 'settings["googleAnalytics.tracker"].value' }
label={ t('institutes.dashboard.c.form.gaTracker') }
component={ TextField }
/>
<Field
required
name={ 'settings["requests.mailto"].value' }
label={ t('institutes.dashboard.c.form.mailto') }
component={ TextField }
/>
<Field
required
name="codeSGSV"
label={ t('institutes.dashboard.c.form.codeSGSV') }
component={ TextField }
validate={ [Validators.maxLength(10)] }
/>
<Field
className="pr-20"
name="uniqueAcceNumbs"
formLabel={ t('institutes.dashboard.c.form.uniqueAcceNumbs') }
labelTrue={ t('institutes.dashboard.c.form.uniqueAcceNumbsTrue') }
labelFalse={ t('institutes.dashboard.c.form.uniqueAcceNumbsFalse') }
component={ BooleanRadioGroup }
validate={ [Validators.required] }
/>
<FormControl>
<FormLabel>
{ t('institutes.dashboard.c.form.materialRequests') }
</FormLabel>
<Field
required
name="allowMaterialRequests"
label={ t('institutes.dashboard.c.form.allowMaterialRequests') }
component={ ReduxCheckbox }
/>
</FormControl>
<Divider/>
<div className="pt-20">
<Button variant="contained" type="submit">{ t('common:action.saveChanges') }</Button>
<Link to={ `/wiews/${wiewsCode || ''}` }>
<Button variant="flat">{ t('common:action.backTo', { where: `${wiewsCode ? t('institutes.common.instDetails', { instCode: wiewsCode }) : t('institutes.common.instList')}` }) }</Button>
</Link>
</div>
</form>
);
};
export default reduxForm({
enableReinitialize: true,
destroyOnUnmount: false,
form: INSTITUTE_FORM,
})(InstituteForm);
......@@ -5,6 +5,7 @@ export class Permissions {
public write: boolean;
public delete: boolean;
public manage: boolean;
public isPublic: boolean;
public getClassname(): string {
return 'org.genesys.blocks.security.serialization.Permissions';
......@@ -30,5 +31,5 @@ export class Permissions {
}
export interface IUserPermissions {
_permissions: Permissions;
_permissions?: Permissions;
}
import Country from 'model/geo/Country';
import { SortDirection } from 'model/Page';
import { IUserPermissions, Permissions } from 'model/acl/ACL';
/*
* Defined in Swagger as '#/definitions/FaoInstitute'
*/
class FaoInstitute {
class FaoInstitute implements IUserPermissions {
public _permissions?: Permissions;
public accessionCount: number;
public acronym: string;
public allowMaterialRequests: boolean;
......@@ -20,6 +23,7 @@ class FaoInstitute {
public maintainsCollection: boolean;
public pgrActivity: boolean;
public safeUrls: string[];
public settings: any;
public type: string;
public uniqueAcceNumbs: boolean;
public url: string;
......@@ -37,6 +41,9 @@ class FaoInstitute {
accessionCount1: { property: 'accessionCount', label: 'Collection size (largest first)', direction: 'DESC' },
accessionCount2: { property: 'accessionCount', label: 'Collection size (smallest first)', direction: 'ASC' },
};
public static clazz: string = 'org.genesys2.server.model.impl.FaoInstitute';
}
export default FaoInstitute;
......@@ -9,7 +9,10 @@ import InstituteDetails from 'model/genesys/InstituteDetails';
import InstituteFilter from 'model/genesys/InstituteFilter';
const URL_LIST = `/api/v1/wiews/list`;
const URL_DETAILS = UrlTemplate.parse(`/api/v1/wiews/{code}`);
const URL_GET = UrlTemplate.parse(`/api/v1/wiews/{code}`);
const URL_DETAILS = UrlTemplate.parse(`/api/v1/wiews/{code}/details`);
const URL_UPDATE = UrlTemplate.parse(`/api/v1/wiews/{code}/update`);
/*
* Defined in Swagger as 'institute'
......@@ -44,7 +47,25 @@ class InstituteService {
}
/**
* details at /api/v1/wiews/{code}
* get at /api/v1/wiews/{code}
*
* @param code code
*/
public static get(code: string): Promise<FaoInstitute> {
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 FaoInstitute);
}
/**
* details at /api/v1/wiews/{code}/details
*
* @param code code
*/
......@@ -61,6 +82,24 @@ class InstituteService {
}).then(({ data }) => data as InstituteDetails);
}
/**
* update at /api/v1/wiews/{code}/update
*
* @param code code
* @param institute institute
*/
public static update(code: string, institute: FaoInstitute): Promise<FaoInstitute> {
const apiUrl = URL_UPDATE.expand({ code });
// console.log(`Fetching from ${apiUrl}`);
const content = { data: institute };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as FaoInstitute);
}
}
......
......@@ -7,7 +7,7 @@ const ReduxCheckbox = ({input: {value, onChange}, label = null, style = {height:
style={ style }
control={
<Checkbox
checked={ !!value }
checked={ !!value && value !== 'false' }
onChange={ onChange }
/>
}
......
......@@ -123,7 +123,7 @@ class Permissions extends React.Component<IPermissionsProps, any> {
}
public render() {
const {autocomplete, variant = 'contained', t } = this.props;
const {autocomplete, variant = 'flat', t } = this.props;
const {aclObjectIdentity, autocompleteObj} = this.state;
return (
......
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