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 @@
"cropTitle": "Crop title",
"otherNames": "Other names",
"version": "Version: {{version: numeric}}",
"alreadyInUse": "Already in use"
"alreadyInUse": "Already in use",
"article": "Updating article in \"{{lang}}\" language"
}
},
"p": {
......
import Crop from 'model/genesys/Crop';
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 navigateTo from 'actions/navigation';
import {createApiCaller, createPureApiCaller} from 'actions/ApiCall';
import {loadCrops} from './public';
import ApiCall from 'model/ApiCall';
import CmsService from 'service/genesys/CmsService';
import Article from 'model/cms/Article';
const receiveCrop = (cropDetails: ApiCall<Crop>): IReducerAction => ({
type: ADMIN_RECEIVE_CROP, payload: cropDetails,
......@@ -21,10 +23,22 @@ const apiSaveCrop = createApiCaller(CropService.saveCrop, ADMIN_RECEIVE_CROP);
const apiDeleteCrop = createPureApiCaller(CropService.deleteCrop);
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));
};
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) => {
return dispatch(apiSaveCrop(crop))
.then((crop) => {
......@@ -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) => {
dispatch(receiveCrop(ApiCall.success(null)));
return dispatch(navigateTo('/c/edit'));
......
......@@ -8,9 +8,11 @@ import Crop from 'model/genesys/Crop';
const INITIAL_STATE: {
crop: ApiCall<Crop>,
cropNames: any,
article: ApiCall<any>,
} = {
crop: null,
cropNames: {},
article: null,
};
export default (state = INITIAL_STATE, action: IReducerAction = {type: ''}) => {
......
......@@ -39,7 +39,8 @@
"cropTitle": "Crop title",
"otherNames": "Other names",
"version": "Version: {{version: numeric}}",
"alreadyInUse": "Already in use"
"alreadyInUse": "Already in use",
"article": "Updating article in \"{{lang}}\" language"
}
},
"p": {
......
......@@ -3,6 +3,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import * as _ from 'lodash';
import { WithTranslation, withTranslation } from 'react-i18next';
import { WithStyles } from '@material-ui/core';
......@@ -10,8 +11,10 @@ import {log} from 'utilities/debug';
import confirm from 'utilities/confirmAlert';
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 Article from 'model/cms/Article';
import CropForm from './c/CropForm';
import PageLayout, {PageContents} from 'ui/layout/PageLayout';
......@@ -20,21 +23,52 @@ import PageTitle from 'ui/common/PageTitle';
interface ICropEditPageProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
shortName?: string;
cropNames: any;
loadCrop: (shortName: string) => void;
loadCrop: (shortName: string) => Promise<Crop>;
saveCrop: (crop: Crop) => Promise<any>;
deleteCrop: (crop: Crop) => void;
navigateTo: (path: string, query?: string) => void;
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> {
public state = {
article: null,
};
protected static needs = [
({params: {shortName}}) => loadCrop(shortName),
];
private onSave = (updatedCrop: Crop) => {
const {saveCrop, navigateTo} = this.props;
private onSave = async (updatedCrop: any) => {
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)
.then(() => navigateTo(`/c/${updatedCrop.shortName}/edit`));
}
......@@ -54,16 +88,24 @@ class CropEditPage extends React.Component<ICropEditPageProps, any> {
}
public componentWillMount() {
const {crop, loadCrop, shortName} = this.props;
const {crop, loadCrop, shortName, loadArticle} = this.props;
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() {
const {shortName, cropNames, t} = this.props;
let {crop} = this.props;
const { shortName, cropNames, t, lang } = this.props;
let { crop } = this.props;
const { article } = this.state;
if (!crop && !shortName) {
crop = new Crop();
......@@ -80,7 +122,17 @@ class CropEditPage extends React.Component<ICropEditPageProps, any> {
<PageContents className="pt-1rem">
<Grid item xs={ 12 }>
<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>
</Grid>
</PageContents>
......@@ -94,6 +146,7 @@ const mapStateToProps = (state, ownProps) => ({
shortName: ownProps.match.params.shortName,
cropNames: state.crop.admin.cropNames,
crop: state.crop.admin.crop ? state.crop.admin.crop.data : undefined,
lang: state.applicationConfig.lang,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
......@@ -101,6 +154,9 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
saveCrop,
deleteCrop,
navigateTo,
loadArticle,
updateArticle,
createArticle,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(withTranslation()(CropEditPage));
......@@ -9,7 +9,7 @@ import {log} from 'utilities/debug';
import Validators from 'utilities/Validators';
import ItemsEditor from 'ui/common/ItemsEditor';
import Divider from '@material-ui/core/Divider';
import HtmlEditField from 'ui/common/forms/HtmlEditField';
const onAddString = () => {
log('Adding new string item');
......@@ -20,7 +20,7 @@ const onRemoveString = (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 &&
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})
<form onSubmit={ handleSubmit }>
{ 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] }/>
<div className="pb-20 pt-20">
<h4>{ t('crop.admin.c.cropForm.otherNames') }</h4>
<ItemsEditor name="otherNames" itemLabel={ t('crop.admin.c.cropForm.otherNames') } addItem={ onAddString } removeItem={ onRemoveString } component={ stringField }/>
</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>
<Divider/>
......
......@@ -3,6 +3,7 @@ import { axiosBackend, apiLanguage } from 'utilities/requestUtils';
import Crop from 'model/genesys/Crop';
import CropDetails from 'model/genesys/CropDetails';
import Article from 'model/cms/Article';
const URL_LIST_CROPS = UrlTemplate.parse(`/api/v1/crops`);
const URL_SAVE_CROP = `/api/v1/crops/save`;
......@@ -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_DETAILS = UrlTemplate.parse(`/api/v1/crops/{shortName}/details`);
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 {
......@@ -129,5 +131,24 @@ export class CropService {
...content,
}).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