Commit cb2189f8 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '152-cms-with-transifex' into 'master'

CMS with Transifex

Closes #152

See merge request genesys-pgr/genesys-ui!157
parents 025a33aa 1c69a421
......@@ -524,7 +524,20 @@
"create": "Create new article"
},
"edit": {
"articleSaved": "Article saved"
"articleSaved": "Article saved",
"transifex": {
"fetch": "Fetch translations",
"fetchStarted": "Fetch started",
"fetchResult": "Result for {{lang, uppercase}}: {{result}}",
"delete": "Remove from transifex",
"deleteStarted": "Started deleting from transifex",
"deleteSucceeded": "Article deleted from transifex successfully",
"deleteFailed": "Deleting article from transifex failed",
"post": "Post to transifex",
"postStarted": "Started posting to transifex",
"postSucceeded": "Article posted to transifex successfully",
"postFailed": "Posting article to transifex failed"
}
}
}
},
......
......@@ -25,7 +25,20 @@
"create": "Create new article"
},
"edit": {
"articleSaved": "Article saved"
"articleSaved": "Article saved",
"transifex": {
"fetch": "Fetch translations",
"fetchStarted": "Fetch started",
"fetchResult": "Result for {{lang, uppercase}}: {{result}}",
"delete": "Remove from transifex",
"deleteStarted": "Started deleting from transifex",
"deleteSucceeded": "Article deleted from transifex successfully",
"deleteFailed": "Deleting article from transifex failed",
"post": "Post to transifex",
"postStarted": "Started posting to transifex",
"postSucceeded": "Article posted to transifex successfully",
"postFailed": "Posting article to transifex failed"
}
}
}
},
......
......@@ -5,9 +5,12 @@ import { translate } from 'react-i18next';
import { withStyles } from '@material-ui/core';
// actions
import { loadArticle, updateArticle, createGlobalArticle } from 'cms/actions/admin';
import { createGlobalArticle, loadArticle, updateArticle } from 'cms/actions/admin';
import { showSnackbar } from 'actions/snackbar';
// model
import Article from 'model/cms/Article';
// service
import CmsService from 'service/genesys/CmsService';
// utilities
import { log } from 'utilities/debug';
// ui
......@@ -17,11 +20,28 @@ import ArticleForm from './c/ArticleForm';
import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton';
import ActionButton from 'ui/common/buttons/ActionButton';
import ArticleSection from '../c/ArticleSection';
import ButtonBar from 'ui/common/buttons/ButtonBar';
const styles = () => ({
article: {
maxWidth: '100%',
},
alertWarning: {
fontSize: 16,
backgroundColor: '#fcf8e3',
color: '#8a6d3b',
border: '1px solid #faebcc',
marginBottom: '1rem',
padding: '.5rem',
},
/*tslint:disable*/
customSubHeader: {
justifyContent: 'space-between' as 'space-between',
'& > div': {
width: 'auto !important' as 'auto !important',
},
},
/*tslint:enable*/
});
interface IArticleEditPageProps extends React.ClassAttributes<any> {
......@@ -36,6 +56,7 @@ interface IArticleEditPageProps extends React.ClassAttributes<any> {
loadArticle: (lang: string, slug, targetId?: number, className?: string) => void;
createGlobalArticle: (article: Article) => void;
updateArticle: (article: Article) => void;
showSnackbar: any;
}
class ArticleEditPage extends React.Component<IArticleEditPageProps, any> {
......@@ -46,7 +67,10 @@ class ArticleEditPage extends React.Component<IArticleEditPageProps, any> {
private constructor(props, context) {
super(props, context);
this.state = { isEdit: true };
this.state = {
isEdit: true,
fetchResults: {},
};
}
private onSave = (article: any) => {
......@@ -76,7 +100,7 @@ class ArticleEditPage extends React.Component<IArticleEditPageProps, any> {
public render() {
const { slug, t, classes } = this.props;
const { isEdit } = this.state;
const { isEdit, fetchResults } = this.state;
let { article } = this.props;
if (!article && !slug) {
......@@ -90,13 +114,32 @@ class ArticleEditPage extends React.Component<IArticleEditPageProps, any> {
return (
<div>
<ContentHeaderWithButton title={ article.title } buttons={
<ActionButton
title={ isEdit ? t('common:action.view') : t('common:action.edit') }
action={ () => this.setState({ isEdit: !isEdit }) }
/>
<ContentHeaderWithButton title={ article.title } classes={ { subHeader: classes.customSubHeader } } buttons={
<ButtonBar>
<ActionButton
title={ isEdit ? t('common:action.view') : t('common:action.edit') }
action={ () => this.setState({ isEdit: !isEdit }) }
/>
<ActionButton
title={ t('cms.admin.p.edit.transifex.fetch') }
action={ this.fetchAllTranslations }
/>
<ActionButton
title={ t('cms.admin.p.edit.transifex.post') }
action={ this.postToTransifex }
/>
<ActionButton
title={ t('cms.admin.p.edit.transifex.delete') }
action={ this.deleteFromTransifex }
/>
</ButtonBar>
}/>
<PageContents className="pt-1rem pb-1rem">
{ fetchResults && Object.keys(fetchResults).length > 0 &&
<div className={ classes.alertWarning }>
{ Object.keys(fetchResults).map((lang) => <div key={ lang }>{ t('cms.admin.p.edit.transifex.fetchResult', {lang, result: fetchResults[lang]}) }</div>) }
</div>
}
<Grid item xs={ 12 }>
{ isEdit ? (
<ArticleForm
......@@ -114,6 +157,37 @@ class ArticleEditPage extends React.Component<IArticleEditPageProps, any> {
);
}
private fetchAllTranslations = () => {
const { article, showSnackbar } = this.props;
this.setState({ fetchResults: {} });
if (!article || !article.slug) {
return this.setState({ fetchResults: { en: 'Article is undefined' } });
}
showSnackbar('cms.admin.p.edit.transifex.fetchStarted');
CmsService.fetchAllFromTransifex(article.slug, article.classPk ? article.classPk.shortName : null, article.targetId, article.template)
.then((fetchResults) => this.setState({ fetchResults }));
}
private postToTransifex = () => {
const { article, showSnackbar } = this.props;
showSnackbar('cms.admin.p.edit.transifex.postStarted');
CmsService.postToTransifex(article.classPk.shortName, article.slug, article.targetId)
.then(() => showSnackbar('cms.admin.p.edit.transifex.postSucceeded'))
.catch(() => showSnackbar('cms.admin.p.edit.transifex.postFailed'));
}
private deleteFromTransifex = () => {
const { article, showSnackbar } = this.props;
showSnackbar('cms.admin.p.edit.transifex.deleteStarted');
CmsService.deleteFromTransifex(article.classPk.shortName, article.slug)
.then(() => showSnackbar('cms.admin.p.edit.transifex.deleteSucceeded'))
.catch(() => showSnackbar('cms.admin.p.edit.transifex.deleteFailed'));
}
}
const mapStateToProps = (state, ownProps) => ({
......@@ -128,6 +202,7 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({
loadArticle,
updateArticle,
createGlobalArticle,
showSnackbar,
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(translate()(ArticleEditPage)));
......@@ -26,6 +26,9 @@ const URL_GET_MENU = UrlTemplate.parse(`/api/v1/cms/menu/{menuKey}`);
const URL_PROCESS_TEMPLATE = `/api/v1/cms/process-template`;
const URL_UPDATE_ACTIVITY_POST = `/api/v1/cms/update-activity-post`;
const URL_UPDATE_ARTICLE = `/api/v1/cms/update-article`;
const URL_POST_TO_TRANSIFEX = `/api/v1/cms/transifex`;
const URL_DELETE_FROM_TRANSIFEX = `/api/v1/cms/transifex`;
const URL_FETCH_ALL_FROM_TRANSIFEX = `/api/v1/cms/transifex/fetchAll`;
const URL_UPDATE_GLOBAL_ARTICLE = `/api/v1/cms/update-global-article`;
const URL_UPDATE_MENU = UrlTemplate.parse(`/api/v1/cms/update-menu/{menuKey}`);
const URL_GET_ARTICLE = UrlTemplate.parse(`/api/v1/cms/{slug}/{clazz}/{targetId}/{language}`);
......@@ -357,6 +360,85 @@ class CmsService {
}).then(({ data }) => data as Article);
}
/**
* postToTransifex at /api/v1/cms/transifex
*
* @param classPkShortName classPkShortName
* @param slug slug
* @param targetId targetId
*/
public static postToTransifex(classPkShortName: string, slug: string, targetId?: number): Promise<Article> {
const qs = QueryString.stringify({
classPkShortName: classPkShortName || undefined,
slug: slug || undefined,
targetId: targetId || undefined,
}, {});
const apiUrl = URL_POST_TO_TRANSIFEX + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as Article);
}
/**
* deleteFromTransifex at /api/v1/cms/transifex
*
* @param classPkShortName classPkShortName
* @param flashAttributes undefined
* @param slug slug
* @param targetId targetId
*/
public static deleteFromTransifex(slug: string, classPkShortName: string, flashAttributes?: object, targetId?: number): Promise<boolean> {
const qs = QueryString.stringify({
classPkShortName: classPkShortName || undefined,
flashAttributes: flashAttributes || undefined,
slug: slug || undefined,
targetId: targetId || undefined,
}, {});
const apiUrl = URL_DELETE_FROM_TRANSIFEX + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* fetchAllFromTransifex at /api/v1/cms/transifex/fetchAll
*
* @param classPkShortName classPkShortName
* @param slug slug
* @param targetId targetId
* @param template template
*/
public static fetchAllFromTransifex(slug: string, classPkShortName?: string, targetId?: number, template?: boolean): Promise<string[]> {
const qs = QueryString.stringify({
classPkShortName: classPkShortName || undefined,
slug: slug || undefined,
targetId: targetId || undefined,
template: template || undefined,
}, {});
const apiUrl = URL_FETCH_ALL_FROM_TRANSIFEX + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as string[]);
}
/**
* updateMenu at /api/v1/cms/update-menu/{menuKey}
*
......
Supports Markdown
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