diff --git a/package.json b/package.json index 8bf8ca4..c064793 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "contenta_react", "version": "0.1.0", "engines": { - "yarn": "0.27.5", - "node": "6.x" + "yarn": "1.12.3", + "node": "10.13.0" }, "private": true, "dependencies": { diff --git a/src/actions/landingPages.js b/src/actions/landingPages.js index 82217a4..c6b81f6 100644 --- a/src/actions/landingPages.js +++ b/src/actions/landingPages.js @@ -14,6 +14,28 @@ export function storeRecipeLandingPage(categories, recipesByCategory) { }; } +export const STORE_FEATURE_LANDING_PAGE = 'LOAD_FEATURE_LANDING_PAGE'; +export function storeFeatureLandingPage(categories, recipesByCategory) { + return { + type: STORE_RECIPE_LANDING_PAGE, + payload: { + categories, + recipesByCategory, + }, + }; +} + +export const STORE_MAGAZINE_LANDING_PAGE = 'LOAD_MAGAZINE_LANDING_PAGE'; +export function storeMagazineLandingPage(categories, recipesByCategory) { + return { + type: STORE_RECIPE_LANDING_PAGE, + payload: { + categories, + recipesByCategory, + }, + }; +} + export function loadRecipeLandingPage() { return function (dispatch) { let pageCategories = []; @@ -46,3 +68,70 @@ export function loadRecipeLandingPage() { }); }; } + +export function loadFeatureLandingPage() { + return function (dispatch) { + let pageCategories = []; + return axios(`${api}/categories`) + .then((result) => { + dispatch(apiActions.storeAPIData(result.data)); + return result.data.data; + }) + .then(categories => categories.map(category => category.id)) + .then((categories) => { + pageCategories = categories; + return Promise.all(categories.map(category => + axios(`${api}/recipes`, { + params: { + 'filter[category.uuid][value]': category, + 'page[limit]': 4, + sort: 'created', + include: 'image,image.thumbnail', + isPromoted: true, + }, + }), + )); + }) + .then((result) => { + result.forEach((recipesInCategory) => { + dispatch(apiActions.storeAPIData(recipesInCategory.data)); + }); + + dispatch(storeFeatureLandingPage(pageCategories, result)); + }); + }; +} + + +export function loadMagazineLandingPage() { + return function (dispatch) { + let pageCategories = []; + return axios(`${api}/categories`) + .then((result) => { + dispatch(apiActions.storeAPIData(result.data)); + return result.data.data; + }) + .then(categories => categories.map(category => category.id)) + .then((categories) => { + pageCategories = categories; + return Promise.all(categories.map(category => + axios(`${api}/recipes`, { + params: { + 'filter[category.uuid][value]': category, + 'page[limit]': 4, + sort: 'created', + include: 'image,image.thumbnail', + isPromoted: true, + }, + }), + )); + }) + .then((result) => { + result.forEach((recipesInCategory) => { + dispatch(apiActions.storeAPIData(recipesInCategory.data)); + }); + + dispatch(storeMagazineLandingPage(pageCategories, result)); + }); + }; +} diff --git a/src/components/02_molecule/Navigation/Navigation.jsx b/src/components/02_molecule/Navigation/Navigation.jsx index b8104e5..8f1fbd8 100644 --- a/src/components/02_molecule/Navigation/Navigation.jsx +++ b/src/components/02_molecule/Navigation/Navigation.jsx @@ -5,9 +5,9 @@ import breakpoints from '../../../styles/breakpoints'; const links = [ ['Home', '/', 'home'], - // ['Features', '/features', 'features'], + ['Features', '/features', 'features'], ['Recipes', '/recipes', 'recipes'], - // ['Magazine', '/magazine', 'magazine'], + ['Magazine', '/magazine', 'magazine'], ]; const Navigation = () => ( diff --git a/src/components/04_template/FeatureLanding/FeatureLanding.jsx b/src/components/04_template/FeatureLanding/FeatureLanding.jsx new file mode 100644 index 0000000..39196e1 --- /dev/null +++ b/src/components/04_template/FeatureLanding/FeatureLanding.jsx @@ -0,0 +1,76 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import * as landingPageActions from '../../../actions/landingPages'; +import * as loadingBarActions from 'react-redux-loading-bar'; +import Default from '../../05_page/Default/Default'; +import TeaserFeatured from '../../03_organism/TeaserFeatured/TeaserFeatured'; +import TeaserList from '../../03_organism/TeaserList/TeaserList'; + +class FeatureLanding extends Component { + componentDidMount() { + if (!this.props.landingPageCategories.length) { + this.props.showLoading(); + this.props.loadFeatureLandingPage(); + } + } + componentWillReceiveProps(nextProps) { + if (nextProps.landingPageCategories.length) { + this.props.hideLoading(); + } + } + render() { + if (this.props.landingPageCategories.length) { + return ( + +
+ + {this.props.landingPageCategories.map(category => ( +
+

{this.props.categories[category.id].title}

+ ({ + id: recipe, + title: this.props.recipes[recipe].title, + subtitle: this.props.recipes[recipe].time > 0 ? `${this.props.recipes[recipe].time}m` : '', + image: this.props.files[this.props.recipes[recipe].image].uri, + }))}/> +
+ ))} + +
+
+ ); + } + return null; + } +} + +FeatureLanding.defaultProps = { + categories: {}, + files: {}, + landingPageCategories: {}, + recipes: {}, +}; + +FeatureLanding.loadData = [landingPageActions.loadFeatureLandingPage]; + +export default connect((state) => ({ + categories: state.api.categories, + files: state.api.files, + landingPageCategories: state.landingPages.categories, + recipes: state.api.recipes, +}), { ...landingPageActions, ...loadingBarActions })(FeatureLanding); diff --git a/src/components/04_template/MagazineLanding/MagazineLanding.jsx b/src/components/04_template/MagazineLanding/MagazineLanding.jsx new file mode 100644 index 0000000..994f763 --- /dev/null +++ b/src/components/04_template/MagazineLanding/MagazineLanding.jsx @@ -0,0 +1,76 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import * as landingPageActions from '../../../actions/landingPages'; +import * as loadingBarActions from 'react-redux-loading-bar'; +import Default from '../../05_page/Default/Default'; +import TeaserFeatured from '../../03_organism/TeaserFeatured/TeaserFeatured'; +import TeaserList from '../../03_organism/TeaserList/TeaserList'; + +class MagazineLanding extends Component { + componentDidMount() { + if (!this.props.landingPageCategories.length) { + this.props.showLoading(); + this.props.loadMagazineLandingPage(); + } + } + componentWillReceiveProps(nextProps) { + if (nextProps.landingPageCategories.length) { + this.props.hideLoading(); + } + } + render() { + if (this.props.landingPageCategories.length) { + return ( + +
+ + {this.props.landingPageCategories.map(category => ( +
+

{this.props.categories[category.id].title}

+ ({ + id: recipe, + title: this.props.recipes[recipe].title, + subtitle: this.props.recipes[recipe].time > 0 ? `${this.props.recipes[recipe].time}m` : '', + image: this.props.files[this.props.recipes[recipe].image].uri, + }))}/> +
+ ))} + +
+
+ ); + } + return null; + } +} + +MagazineLanding.defaultProps = { + categories: {}, + files: {}, + landingPageCategories: {}, + recipes: {}, +}; + +MagazineLanding.loadData = [landingPageActions.loadMagazineLandingPage]; + +export default connect((state) => ({ + categories: state.api.categories, + files: state.api.files, + landingPageCategories: state.landingPages.categories, + recipes: state.api.recipes, +}), { ...landingPageActions, ...loadingBarActions })(MagazineLanding); diff --git a/src/routes.js b/src/routes.js index 342bd88..1cd939e 100644 --- a/src/routes.js +++ b/src/routes.js @@ -5,7 +5,9 @@ * It returns an array of Redux Thunks. */ import Home from './components/04_template/Home/Home'; +import FeatureLanding from './components/04_template/FeatureLanding/FeatureLanding'; import RecipeLanding from './components/04_template/RecipeLanding/RecipeLanding'; +import MagazineLanding from './components/04_template/MagazineLanding/MagazineLanding'; const routes = [ { @@ -14,12 +16,24 @@ const routes = [ exact: true, strict: true, }, + { + path: '/features', + component: FeatureLanding, + exact: true, + strict: true, + }, { path: '/recipes', component: RecipeLanding, exact: true, strict: true, }, + { + path: '/magazine', + component: MagazineLanding, + exact: true, + strict: true, + }, ]; export default routes;