Commit b7ce1d80 authored by Oleksii Savran's avatar Oleksii Savran Committed by Viacheslav Pavlov

Edit crop text

-added updating articles in different languages
-Added method to crop service
-fixed ssr loading of article on edit page
-silent error for articles
-small bug fixes with non default languages
-removed article from redux state
parent 21e5d9c8
...@@ -1072,7 +1072,8 @@ ...@@ -1072,7 +1072,8 @@
"cropTitle": "Crop title", "cropTitle": "Crop title",
"otherNames": "Other names", "otherNames": "Other names",
"version": "Version: {{version: numeric}}", "version": "Version: {{version: numeric}}",
"alreadyInUse": "Already in use" "alreadyInUse": "Already in use",
"article": "Updating article in \"{{lang}}\" language"
} }
}, },
"p": { "p": {
......
import Crop from 'model/genesys/Crop'; import Crop from 'model/genesys/Crop';
import {IReducerAction} from 'model/common.model'; import {IReducerAction} from 'model/common.model';
import {ADMIN_RECEIVE_CROP, UPDATE_CROPS_LIST} from 'crop/constants'; import { ADMIN_RECEIVE_CROP, UPDATE_CROPS_LIST } from 'crop/constants';
import {CropService} from 'service/CropService'; import {CropService} from 'service/CropService';
import navigateTo from 'actions/navigation'; import navigateTo from 'actions/navigation';
import {createApiCaller, createPureApiCaller} from 'actions/ApiCall'; import {createApiCaller, createPureApiCaller} from 'actions/ApiCall';
import {loadCrops} from './public'; import {loadCrops} from './public';
import ApiCall from 'model/ApiCall'; import ApiCall from 'model/ApiCall';
import CmsService from 'service/genesys/CmsService';
import Article from 'model/cms/Article';
const receiveCrop = (cropDetails: ApiCall<Crop>): IReducerAction => ({ const receiveCrop = (cropDetails: ApiCall<Crop>): IReducerAction => ({
type: ADMIN_RECEIVE_CROP, payload: cropDetails, type: ADMIN_RECEIVE_CROP, payload: cropDetails,
...@@ -21,10 +23,22 @@ const apiSaveCrop = createApiCaller(CropService.saveCrop, ADMIN_RECEIVE_CROP); ...@@ -21,10 +23,22 @@ const apiSaveCrop = createApiCaller(CropService.saveCrop, ADMIN_RECEIVE_CROP);
const apiDeleteCrop = createPureApiCaller(CropService.deleteCrop); const apiDeleteCrop = createPureApiCaller(CropService.deleteCrop);
const apiRelinkAccessions = createPureApiCaller(CropService.relinkAccessions); const apiRelinkAccessions = createPureApiCaller(CropService.relinkAccessions);
export const loadCrop = (shortName: string) => (dispatch) => { const apiLoadArticle = createPureApiCaller(CmsService.getArticle, {silentError: true});
const apiUpdateArticle = createPureApiCaller(CropService.updateBlurb);
export const loadCrop = (shortName: string) => (dispatch, getState) => {
return dispatch(apiLoadCrop(shortName)); return dispatch(apiLoadCrop(shortName));
}; };
export const loadArticle = (id: number) => (dispatch, getState) => {
const language = getState().applicationConfig.lang;
return dispatch(apiLoadArticle('org.genesys2.server.model.impl.Crop', language, 'blurb', id, false)).catch((e) => {
console.log('load article error', e);
});
};
export const saveCrop = (crop: Crop) => (dispatch) => { export const saveCrop = (crop: Crop) => (dispatch) => {
return dispatch(apiSaveCrop(crop)) return dispatch(apiSaveCrop(crop))
.then((crop) => { .then((crop) => {
...@@ -32,6 +46,19 @@ export const saveCrop = (crop: Crop) => (dispatch) => { ...@@ -32,6 +46,19 @@ export const saveCrop = (crop: Crop) => (dispatch) => {
}); });
}; };
export const updateArticle = (article: Article, shortName: string) => (dispatch) => {
return dispatch(apiUpdateArticle(article, shortName));
};
export const createArticle = (article: Article, shortName: string) => (dispatch, getState) => {
const language = getState().applicationConfig.lang;
article.lang = language;
return dispatch(apiUpdateArticle(article, shortName));
};
export const createCrop = () => (dispatch) => { export const createCrop = () => (dispatch) => {
dispatch(receiveCrop(ApiCall.success(null))); dispatch(receiveCrop(ApiCall.success(null)));
return dispatch(navigateTo('/c/edit')); return dispatch(navigateTo('/c/edit'));
......
...@@ -8,9 +8,11 @@ import Crop from 'model/genesys/Crop'; ...@@ -8,9 +8,11 @@ import Crop from 'model/genesys/Crop';
const INITIAL_STATE: { const INITIAL_STATE: {
crop: ApiCall<Crop>, crop: ApiCall<Crop>,
cropNames: any, cropNames: any,
article: ApiCall<any>,
} = { } = {
crop: null, crop: null,
cropNames: {}, cropNames: {},
article: null,
}; };
export default (state = INITIAL_STATE, action: IReducerAction = {type: ''}) => { export default (state = INITIAL_STATE, action: IReducerAction = {type: ''}) => {
......
...@@ -39,7 +39,8 @@ ...@@ -39,7 +39,8 @@
"cropTitle": "Crop title", "cropTitle": "Crop title",
"otherNames": "Other names", "otherNames": "Other names",
"version": "Version: {{version: numeric}}", "version": "Version: {{version: numeric}}",
"alreadyInUse": "Already in use" "alreadyInUse": "Already in use",
"article": "Updating article in \"{{lang}}\" language"
} }
}, },
"p": { "p": {
......
...@@ -3,6 +3,7 @@ import {connect} from 'react-redux'; ...@@ -3,6 +3,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux'; import {bindActionCreators} from 'redux';
import Grid from '@material-ui/core/Grid'; import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper'; import Paper from '@material-ui/core/Paper';
import * as _ from 'lodash';
import { WithTranslation, withTranslation } from 'react-i18next'; import { WithTranslation, withTranslation } from 'react-i18next';
import { WithStyles } from '@material-ui/core'; import { WithStyles } from '@material-ui/core';
...@@ -10,8 +11,10 @@ import {log} from 'utilities/debug'; ...@@ -10,8 +11,10 @@ import {log} from 'utilities/debug';
import confirm from 'utilities/confirmAlert'; import confirm from 'utilities/confirmAlert';
import navigateTo from 'actions/navigation'; import navigateTo from 'actions/navigation';
import {deleteCrop, loadCrop, saveCrop} from 'crop/actions/admin'; import {deleteCrop, loadCrop, saveCrop, loadArticle, updateArticle, createArticle} from 'crop/actions/admin';
import Crop from 'model/genesys/Crop'; import Crop from 'model/genesys/Crop';
import Article from 'model/cms/Article';
import CropForm from './c/CropForm'; import CropForm from './c/CropForm';
import PageLayout, {PageContents} from 'ui/layout/PageLayout'; import PageLayout, {PageContents} from 'ui/layout/PageLayout';
...@@ -20,21 +23,52 @@ import PageTitle from 'ui/common/PageTitle'; ...@@ -20,21 +23,52 @@ import PageTitle from 'ui/common/PageTitle';
interface ICropEditPageProps extends React.ClassAttributes<any>, WithTranslation, WithStyles { interface ICropEditPageProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
shortName?: string; shortName?: string;
cropNames: any; cropNames: any;
loadCrop: (shortName: string) => void; loadCrop: (shortName: string) => Promise<Crop>;
saveCrop: (crop: Crop) => Promise<any>; saveCrop: (crop: Crop) => Promise<any>;
deleteCrop: (crop: Crop) => void; deleteCrop: (crop: Crop) => void;
navigateTo: (path: string, query?: string) => void; navigateTo: (path: string, query?: string) => void;
crop: Crop; crop: Crop;
article: Article;
loadArticle: (id: number) => Promise<Article>;
updateArticle: (article: Article, shortName: string) => Promise<Article>;
createArticle: (article: Article, shortName: string) => Promise<Article>;
lang: string;
} }
class CropEditPage extends React.Component<ICropEditPageProps, any> { class CropEditPage extends React.Component<ICropEditPageProps, any> {
public state = {
article: null,
};
protected static needs = [ protected static needs = [
({params: {shortName}}) => loadCrop(shortName), ({params: {shortName}}) => loadCrop(shortName),
]; ];
private onSave = (updatedCrop: Crop) => { private onSave = async (updatedCrop: any) => {
const {saveCrop, navigateTo} = this.props; const { article, createArticle, updateArticle, saveCrop, navigateTo, shortName } = this.props;
const { article: editedArticle } = updatedCrop;
const { id } = this.props.crop;
let receivedArticle: Article;
try {
if (article && !_.isEqual(editedArticle, article.body)) {
receivedArticle = await updateArticle({ ...article, ...{ body: editedArticle } }, shortName);
} else if (!article && editedArticle) {
const article = new Article();
article.body = editedArticle;
article.targetId = id;
receivedArticle = await createArticle(article, shortName);
}
if (receivedArticle) {
this.setState({article: receivedArticle});
}
} catch (e) {
return console.log('updating description error', e);
}
delete updatedCrop.article;
saveCrop(updatedCrop) saveCrop(updatedCrop)
.then(() => navigateTo(`/c/${updatedCrop.shortName}/edit`)); .then(() => navigateTo(`/c/${updatedCrop.shortName}/edit`));
} }
...@@ -54,16 +88,24 @@ class CropEditPage extends React.Component<ICropEditPageProps, any> { ...@@ -54,16 +88,24 @@ class CropEditPage extends React.Component<ICropEditPageProps, any> {
} }
public componentWillMount() { public componentWillMount() {
const {crop, loadCrop, shortName} = this.props; const {crop, loadCrop, shortName, loadArticle} = this.props;
if (shortName && (!crop || crop.shortName !== shortName)) { if (shortName && (!crop || crop.shortName !== shortName)) {
loadCrop(shortName); loadCrop(shortName).then((crop: Crop) => {
loadArticle(crop.id).then((article: Article) => {
console.log(article);
this.setState({article})
}).catch((e) => {
console.log('load article error: ', e);
})
});
} }
} }
public render() { public render() {
const {shortName, cropNames, t} = this.props; const { shortName, cropNames, t, lang } = this.props;
let {crop} = this.props; let { crop } = this.props;
const { article } = this.state;
if (!crop && !shortName) { if (!crop && !shortName) {
crop = new Crop(); crop = new Crop();
...@@ -80,7 +122,17 @@ class CropEditPage extends React.Component<ICropEditPageProps, any> { ...@@ -80,7 +122,17 @@ class CropEditPage extends React.Component<ICropEditPageProps, any> {
<PageContents className="pt-1rem"> <PageContents className="pt-1rem">
<Grid item xs={ 12 }> <Grid item xs={ 12 }>
<Paper className="p-20 mb-10"> <Paper className="p-20 mb-10">
<CropForm initialValues={ crop } onSubmit={ this.onSave } onDelete={ this.onDelete } cropNames={ cropNames } t={ t }/> <CropForm
initialValues={ {
...crop,
article: article ? article.body : null,
} }
onSubmit={ this.onSave }
onDelete={ this.onDelete }
cropNames={ cropNames }
t={ t }
lang={ lang }
/>
</Paper> </Paper>
</Grid> </Grid>
</PageContents> </PageContents>
...@@ -94,6 +146,7 @@ const mapStateToProps = (state, ownProps) => ({ ...@@ -94,6 +146,7 @@ const mapStateToProps = (state, ownProps) => ({
shortName: ownProps.match.params.shortName, shortName: ownProps.match.params.shortName,
cropNames: state.crop.admin.cropNames, cropNames: state.crop.admin.cropNames,
crop: state.crop.admin.crop ? state.crop.admin.crop.data : undefined, crop: state.crop.admin.crop ? state.crop.admin.crop.data : undefined,
lang: state.applicationConfig.lang,
}); });
const mapDispatchToProps = (dispatch) => bindActionCreators({ const mapDispatchToProps = (dispatch) => bindActionCreators({
...@@ -101,6 +154,9 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ ...@@ -101,6 +154,9 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
saveCrop, saveCrop,
deleteCrop, deleteCrop,
navigateTo, navigateTo,
loadArticle,
updateArticle,
createArticle,
}, dispatch); }, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(withTranslation()(CropEditPage)); export default connect(mapStateToProps, mapDispatchToProps)(withTranslation()(CropEditPage));
...@@ -9,7 +9,7 @@ import {log} from 'utilities/debug'; ...@@ -9,7 +9,7 @@ import {log} from 'utilities/debug';
import Validators from 'utilities/Validators'; import Validators from 'utilities/Validators';
import ItemsEditor from 'ui/common/ItemsEditor'; import ItemsEditor from 'ui/common/ItemsEditor';
import Divider from '@material-ui/core/Divider'; import Divider from '@material-ui/core/Divider';
import HtmlEditField from 'ui/common/forms/HtmlEditField';
const onAddString = () => { const onAddString = () => {
log('Adding new string item'); log('Adding new string item');
...@@ -20,7 +20,7 @@ const onRemoveString = (item) => { ...@@ -20,7 +20,7 @@ const onRemoveString = (item) => {
log('Removing string', item); log('Removing string', item);
}; };
const CropForm = ({error, handleSubmit, initialValues, onDelete, cropNames, t}) => { const CropForm = ({error, handleSubmit, initialValues, onDelete, cropNames, t, lang}) => {
const notInUse = (value) => !value || cropNames && const notInUse = (value) => !value || cropNames &&
Object.keys(cropNames).filter((cropShortName) => cropNames[cropShortName].indexOf(value) !== -1 && cropShortName !== initialValues.shortName).length === 0 Object.keys(cropNames).filter((cropShortName) => cropNames[cropShortName].indexOf(value) !== -1 && cropShortName !== initialValues.shortName).length === 0
...@@ -35,13 +35,21 @@ const CropForm = ({error, handleSubmit, initialValues, onDelete, cropNames, t}) ...@@ -35,13 +35,21 @@ const CropForm = ({error, handleSubmit, initialValues, onDelete, cropNames, t})
<form onSubmit={ handleSubmit }> <form onSubmit={ handleSubmit }>
{ initialValues.version >= 0 ? <div>{ t(`crop.admin.c.cropForm.version`, {version: initialValues.version}) }</div> : null } { initialValues.version >= 0 ? <div>{ t(`crop.admin.c.cropForm.version`, {version: initialValues.version}) }</div> : null }
<Field disabled={ initialValues.shortName } required name="shortName" label={ t('crop.admin.c.cropForm.cropCode') } component={ TextField } validate={ [Validators.required, Validators.maxLength20] }/> <Field disabled={ !!initialValues.shortName } required name="shortName" label={ t('crop.admin.c.cropForm.cropCode') } component={ TextField } validate={ [Validators.required, Validators.maxLength20] }/>
<Field required name="name" label={ t('crop.admin.c.cropForm.cropTitle') } component={ TextField } validate={ [Validators.required] }/> <Field required name="name" label={ t('crop.admin.c.cropForm.cropTitle') } component={ TextField } validate={ [Validators.required] }/>
<div className="pb-20 pt-20"> <div className="pb-20 pt-20">
<h4>{ t('crop.admin.c.cropForm.otherNames') }</h4> <h4>{ t('crop.admin.c.cropForm.otherNames') }</h4>
<ItemsEditor name="otherNames" itemLabel={ t('crop.admin.c.cropForm.otherNames') } addItem={ onAddString } removeItem={ onRemoveString } component={ stringField }/> <ItemsEditor name="otherNames" itemLabel={ t('crop.admin.c.cropForm.otherNames') } addItem={ onAddString } removeItem={ onRemoveString } component={ stringField }/>
</div> </div>
<div className="mt-15">
<h4>{ t('crop.admin.c.cropForm.article', {lang}) }</h4>
<Field
required
name="article"
component={ HtmlEditField }
/>
</div>
<div>{ error && <strong>{ error }</strong> }</div> <div>{ error && <strong>{ error }</strong> }</div>
<Divider/> <Divider/>
......
...@@ -3,6 +3,7 @@ import { axiosBackend, apiLanguage } from 'utilities/requestUtils'; ...@@ -3,6 +3,7 @@ import { axiosBackend, apiLanguage } from 'utilities/requestUtils';
import Crop from 'model/genesys/Crop'; import Crop from 'model/genesys/Crop';
import CropDetails from 'model/genesys/CropDetails'; import CropDetails from 'model/genesys/CropDetails';
import Article from 'model/cms/Article';
const URL_LIST_CROPS = UrlTemplate.parse(`/api/v1/crops`); const URL_LIST_CROPS = UrlTemplate.parse(`/api/v1/crops`);
const URL_SAVE_CROP = `/api/v1/crops/save`; const URL_SAVE_CROP = `/api/v1/crops/save`;
...@@ -10,6 +11,7 @@ const URL_DELETE_CROP = UrlTemplate.parse(`/api/v1/crops/{shortName}`); ...@@ -10,6 +11,7 @@ const URL_DELETE_CROP = UrlTemplate.parse(`/api/v1/crops/{shortName}`);
const URL_GET_CROP = UrlTemplate.parse(`/api/v1/crops/{shortName}`); const URL_GET_CROP = UrlTemplate.parse(`/api/v1/crops/{shortName}`);
const URL_GET_CROP_DETAILS = UrlTemplate.parse(`/api/v1/crops/{shortName}/details`); const URL_GET_CROP_DETAILS = UrlTemplate.parse(`/api/v1/crops/{shortName}/details`);
const URL_RELINK_ACCESSIONS = UrlTemplate.parse(`/api/v1/crops/{shortName}/relink?accessions`); const URL_RELINK_ACCESSIONS = UrlTemplate.parse(`/api/v1/crops/{shortName}/relink?accessions`);
const URL_UPDATE_CROP_BLURB = UrlTemplate.parse(`/api/v1/crops/{shortName}/blurb`);
export class CropService { export class CropService {
...@@ -129,5 +131,24 @@ export class CropService { ...@@ -129,5 +131,24 @@ export class CropService {
...content, ...content,
}).then(({ }) => true); }).then(({ }) => true);
} }
/**
* updateBlurb at /api/v1/crops/{shortName}/blurb
*
* @param article Article
* @param shortName shortName
*/
public static updateBlurb(article: Article, shortName: string, xhrConfig?): Promise<Article> {
const apiUrl = URL_UPDATE_CROP_BLURB.expand({shortName});
const content = { data: article };
return axiosBackend.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as Article);
}
} }
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