Commit add7dd4b authored by Matija Obreza's avatar Matija Obreza

CMS module

- added content page
- models in cms/ folder
- Article section takes all height
parent f40d475d
......@@ -47,6 +47,21 @@
"login": "Login",
"register": "Registration"
},
"content": {
"menu": {
"about": "About Genesys",
"contact": "Contact us",
"what-is-genesys": "What is Genesys?",
"history-of-genesys": "History of Genesys",
"newsletter": "Genesys Newsletter",
"faq": "Frequently asked questions",
"how-to-use-genesys": "How to use Genesys?",
"disclaimer": "Disclaimer",
"terms": "Terms and Conditions of Use",
"copying": "Copyright policy",
"privacy": "Privacy policy"
}
},
"p": {
"crop": {
"subtitle": "List of crops registered with Genesys",
......
import ContentPage from 'cms/ui/ContentPage';
const publicRoutes = [
{
path: '/content/:menuItem/:slug',
component: ContentPage,
},
];
export {publicRoutes as cmsPublicRoutes};
import * as React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import { translate } from 'react-i18next';
// Service
import CmsService from 'service/genesys/CmsService';
// UI
import PageLayout from 'ui/layout/PageLayout';
import ContentHeader from 'ui/common/heading/ContentHeader';
import ArticleSection from './c/ArticleSection';
import MenuStepper from './c/MenuStepper';
import Grid from '@material-ui/core/Grid';
interface IContentPageProps extends React.ClassAttributes<any> {
menuItem: string;
slug: string;
t: any;
i18n: any;
}
class ContentPage extends React.Component<IContentPageProps> {
public state = {
menu: null,
article: null,
};
public componentWillMount() {
const { menuItem, slug, i18n } = this.props;
CmsService.getMenu(menuItem).then((menu) => this.setState({menu}));
CmsService.getArticleBySlugAndLang(slug, i18n.language).then((article) => this.setState({article}));
}
public componentWillReceiveProps(nextProps) {
const {menuItem: oldMenu} = this.props;
const { menuItem, slug, i18n } = nextProps;
if (!oldMenu || oldMenu !== menuItem) {
CmsService.getMenu(menuItem).then((menu) => this.setState({menu}));
}
CmsService.getArticleBySlugAndLang(slug, i18n.language).then((article) => this.setState({article}));
}
public render() {
const { t } = this.props;
const {article, menu} = this.state;
return (
<PageLayout>
{ article &&
<Grid container>
<ContentHeader title={ article.title } />
<ArticleSection article={ article }/>
<MenuStepper menu={ menu } t={ t }/>
</Grid>
}
</PageLayout>
);
}
}
const mapStateToProps = (state, ownProps) => ({
menuItem: ownProps.match.params.menuItem,
slug: ownProps.match.params.slug,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
// not now
}, dispatch);
export default translate()(connect(mapStateToProps, mapDispatchToProps)(ContentPage));
import * as React from 'react';
import withStyles from '@material-ui/core/styles/withStyles';
import Article from 'model/cms/Article';
import Grid from '@material-ui/core/Grid';
const styles = () => ({
/*tslint:disable*/
root: {
minHeight: 'calc(100vh - 365px)',
backgroundColor: 'white',
padding: '33px 40px 49px 40px',
'& > p': {
fontFamily: 'Roboto-Light',
fontSize: '24px',
color: '#4d4c46',
lineHeight: '36px',
margin: 0,
},
'& > h3': {
fontSize: '24px',
marginTop: '28px',
paddingBottom: '6px',
},
'& > ul': {
marginTop: '30px',
padding: 0,
paddingLeft: '11px',
},
'& > ul > li': {
fontFamily: 'Roboto-Regular',
fontSize: '18px',
lineHeight: '30px',
},
},
/*tslint:enable*/
});
interface IArticleSectionProps extends React.ClassAttributes<any> {
article: Article;
classes: any;
}
class ArticleSection extends React.Component<IArticleSectionProps> {
public render() {
const {article, classes} = this.props;
return(
<Grid item xs={ 9 }>
<div className={ classes.root } dangerouslySetInnerHTML={ {__html: article.body} }/>
</Grid>
);
}
}
export default withStyles(styles)(ArticleSection);
import * as React from 'react';
import withStyles from '@material-ui/core/styles/withStyles';
import { Link } from 'react-router-dom';
import MenuItem from 'model/cms/MenuItem';
import Menu from 'model/cms/Menu';
import Grid from '@material-ui/core/Grid';
const styles = () => ({
root: {
padding: '0 20px',
},
/*tslint:disable*/
menuItem: {
borderBottom: '1px solid #d5d4d3',
padding: '12px 9px 11px',
lineHeight: '26px',
fontSize: '16px',
'&:hover': {
backgroundColor: '#d5d4d3',
},
},
/*tslint:enable*/
});
interface IMenuStepperProps extends React.ClassAttributes<any> {
menu: Menu;
classes: any;
t?: any;
}
class MenuStepper extends React.Component<IMenuStepperProps> {
public render() {
const { menu, classes, t } = this.props;
return !menu ? null : (
<Grid item xs={ 3 } className={ classes.root }>
{ menu.items.map((menuItem: MenuItem) => (
<Link to={ menuItem.url }>
<div className={ classes.menuItem }>
{ t(`content.${menuItem.text}`) }
</div>
</Link>
))
}
</Grid>
);
}
}
export default withStyles(styles)(MenuStepper);
/*
* Defined in Swagger as '#/definitions/ActivityPost'
*/
class ActivityPost {
public body: string;
public createdBy: number;
public createdDate: Date;
public id: number;
public lastModifiedBy: number;
public lastModifiedDate: Date;
public postDate: Date;
public title: string;
}
export default ActivityPost;
/*
* Defined in Swagger as '#/definitions/Article'
*/
class Article {
public body: string;
public createdBy: number;
public createdDate: Date;
public id: number;
public lang: string;
public lastModifiedBy: number;
public lastModifiedDate: Date;
public postDate: Date;
public slug: string;
public summary: string;
public targetId: number;
public template: boolean;
public title: string;
}
export default Article;
import StringFilter from 'model/StringFilter';
/*
* Defined in Swagger as '#/definitions/ArticleFilter'
*/
class ArticleFilter {
public NOT: ArticleFilter;
public NOTNULL: string[];
public NULL: string[];
public id: number[];
public lang: string[];
public slug: string[];
public template: boolean;
public title: StringFilter;
}
export default ArticleFilter;
import MenuItem from 'model/cms/MenuItem';
/*
* Defined in Swagger as '#/definitions/Menu'
*/
class Menu {
public id: number;
public items: MenuItem[];
public key: string;
}
export default Menu;
import Menu from 'model/cms/Menu';
/*
* Defined in Swagger as '#/definitions/MenuItem'
*/
class MenuItem {
public active: boolean;
public createdBy: number;
public createdDate: Date;
public id: number;
public lastModifiedBy: number;
public lastModifiedDate: Date;
public menu: Menu;
public orderIndex: number;
public target: string;
public text: string;
public title: string;
public url: string;
public version: number;
}
export default MenuItem;
import * as UrlTemplate from 'url-template';
import * as QueryString from 'query-string';
import { axiosBackend } from 'utilities/requestUtils';
import ActivityPost from 'model/cms/ActivityPost';
import Article from 'model/cms/Article';
import ArticleFilter from 'model/cms/ArticleFilter';
import FilteredPage from 'model/FilteredPage';
import Menu from 'model/cms/Menu';
import MenuItem from 'model/cms/MenuItem';
import Page from 'model/Page';
const URL_GET_ACTIVITY_POST = UrlTemplate.parse(`/api/v1/cms/activity-post/{id}`);
const URL_GET_ALL_NEWS = `/api/v1/cms/all-news`;
const URL_GET_ARTICLE_BY_SLUG_AND_LANG = UrlTemplate.parse(`/api/v1/cms/article/{slug}/{language}`);
const URL_CREATE_GLOBAL_ARTICLE = `/api/v1/cms/create-article`;
const URL_CREATE_ACTIVITY_POST = `/api/v1/cms/create-post`;
const URL_DELETE_ACTIVITY_POST = UrlTemplate.parse(`/api/v1/cms/delete-post/{id}`);
const URL_ENSURE_MENU_ITEM = `/api/v1/cms/ensure-menu-item`;
const URL_GET_GLOBAL_ARTICLE = UrlTemplate.parse(`/api/v1/cms/global-article/{slug}/{language}`);
const URL_GET_LAST_NEWS = `/api/v1/cms/last-news`;
const URL_LIST_ARTICLES = `/api/v1/cms/list`;
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_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}`);
/*
* Defined in Swagger as 'cms'
*/
class CmsService {
/**
* getActivityPost at /api/v1/cms/activity-post/{id}
*
* @param id id
*/
public static getActivityPost(id: number): Promise<ActivityPost> {
const apiUrl = URL_GET_ACTIVITY_POST.expand({id});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as ActivityPost);
}
/**
* getAllNews at /api/v1/cms/all-news
*
* @param p p
*/
public static getAllNews(p?: number): Promise<Page<ActivityPost>> {
const qs = QueryString.stringify({
p: p || undefined,
}, {});
const apiUrl = URL_GET_ALL_NEWS + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Page<ActivityPost>);
}
/**
* getArticleBySlugAndLang at /api/v1/cms/article/{slug}/{language}
*
* @param language language
* @param slug slug
*/
public static getArticleBySlugAndLang(slug: string, language: string): Promise<Article> {
const apiUrl = URL_GET_ARTICLE_BY_SLUG_AND_LANG.expand({slug, language});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Article);
}
/**
* createGlobalArticle at /api/v1/cms/create-article
*
* @param article article
*/
public static createGlobalArticle(article: Article): Promise<Article> {
const apiUrl = URL_CREATE_GLOBAL_ARTICLE;
// console.log(`Fetching from ${apiUrl}`);
const content = { data: article };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as Article);
}
/**
* createActivityPost at /api/v1/cms/create-post
*
* @param post post
*/
public static createActivityPost(post: ActivityPost): Promise<ActivityPost> {
const apiUrl = URL_CREATE_ACTIVITY_POST;
// console.log(`Fetching from ${apiUrl}`);
const content = { data: post };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as ActivityPost);
}
/**
* deleteActivityPost at /api/v1/cms/delete-post/{id}
*
* @param id id
*/
public static deleteActivityPost(id: number): Promise<boolean> {
const apiUrl = URL_DELETE_ACTIVITY_POST.expand({id});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'DELETE',
...content,
}).then(({ data }) => data as boolean);
}
/**
* ensureMenuItem at /api/v1/cms/ensure-menu-item
*
* @param menuKey menuKey
* @param text text
* @param url url
*/
public static ensureMenuItem(menuKey: string, text: string, url: string): Promise<MenuItem> {
const qs = QueryString.stringify({
menuKey: menuKey || undefined,
text: text || undefined,
url: url || undefined,
}, {});
const apiUrl = URL_ENSURE_MENU_ITEM + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as MenuItem);
}
/**
* getGlobalArticle at /api/v1/cms/global-article/{slug}/{language}
*
* @param language language
* @param slug slug
* @param useDefault useDefault
*/
public static getGlobalArticle(language: string, slug: string, useDefault?: boolean): Promise<Article> {
const qs = QueryString.stringify({
useDefault: useDefault || undefined,
}, {});
const apiUrl = URL_GET_GLOBAL_ARTICLE.expand({language, slug}) + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Article);
}
/**
* getLastNews at /api/v1/cms/last-news
*
*/
public static getLastNews(): Promise<ActivityPost[]> {
const apiUrl = URL_GET_LAST_NEWS;
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as ActivityPost[]);
}
/**
* listArticles at /api/v1/cms/list
*
* @param filter filter
* @param d d
* @param f f
* @param l l
* @param p p
* @param s s
*/
public static listArticles(filter?: ArticleFilter, d?: string, f?: string, l?: number, p?: number, s?: string[]): Promise<FilteredPage<Article>> {
const qs = QueryString.stringify({
d: d || undefined,
f: f || undefined,
l: l || undefined,
p: p || undefined,
s: s || undefined,
}, {});
const apiUrl = URL_LIST_ARTICLES + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { data: filter };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as FilteredPage<Article>);
}
/**
* getMenu at /api/v1/cms/menu/{menuKey}
*
* @param menuKey menuKey
*/
public static getMenu(menuKey: string): Promise<Menu> {
const apiUrl = URL_GET_MENU.expand({menuKey});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Menu);
}
/**
* processTemplate at /api/v1/cms/process-template
*
* @param root root
* @param body body
*/
public static processTemplate(root: undefined, body: string): Promise<string> {
const qs = QueryString.stringify({
body: body || undefined,
}, {});
const apiUrl = URL_PROCESS_TEMPLATE + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { data: root };
return axiosBackend.request({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as string);
}
/**
* updateActivityPost at /api/v1/cms/update-activity-post