fetch environments in App container for active link routing

This commit is contained in:
cyril lopez 2017-07-17 17:44:19 +02:00
parent 92308fccbf
commit 74e90d271b
15 changed files with 94 additions and 92 deletions

View File

@ -14,7 +14,7 @@ class PluginLeftMenu extends React.Component { // eslint-disable-line react/pref
return ( return (
<div className={`${styles.pluginLeftMenu} col-md-3`}> <div className={`${styles.pluginLeftMenu} col-md-3`}>
{map(this.props.sections, (section, index) => ( {map(this.props.sections, (section, index) => (
<PluginLeftMenuSection key={index} section={section} /> <PluginLeftMenuSection key={index} section={section} environments={this.props.environments} />
))} ))}
</div> </div>
); );
@ -22,6 +22,7 @@ class PluginLeftMenu extends React.Component { // eslint-disable-line react/pref
} }
PluginLeftMenu.propTypes = { PluginLeftMenu.propTypes = {
environments: React.PropTypes.array,
sections: React.PropTypes.array.isRequired, sections: React.PropTypes.array.isRequired,
}; };

View File

@ -7,13 +7,22 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { includes, isEmpty } from 'lodash';
import styles from './styles.scss'; import styles from './styles.scss';
import config from './config.json';
class PluginLeftMenuLink extends React.Component { // eslint-disable-line react/prefer-stateless-function class PluginLeftMenuLink extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() { render() {
let url;
if (!isEmpty(this.props.environments)) {
url = includes(config.environmentsRequired, this.props.link.slug) ?
`${this.props.link.slug}/${this.props.environments[0].name}`
: `${this.props.link.slug}`;
}
return ( return (
<li className={styles.pluginLeftMenuLink}> <li className={styles.pluginLeftMenuLink}>
<Link className={styles.link} to={`/plugins/settings-manager/${this.props.link.slug}`} activeClassName={styles.linkActive}> <Link className={styles.link} to={`/plugins/settings-manager/${url}`} activeClassName={styles.linkActive}>
<i className={`fa fa-${this.props.link.icon}`} /> <i className={`fa fa-${this.props.link.icon}`} />
<span><FormattedMessage {...{id: this.props.link.name}} /></span> <span><FormattedMessage {...{id: this.props.link.name}} /></span>
</Link> </Link>
@ -23,6 +32,7 @@ class PluginLeftMenuLink extends React.Component { // eslint-disable-line react/
} }
PluginLeftMenuLink.propTypes = { PluginLeftMenuLink.propTypes = {
environments: React.PropTypes.array,
link: React.PropTypes.object.isRequired, link: React.PropTypes.object.isRequired,
}; };

View File

@ -17,6 +17,7 @@ class PluginLeftMenuSection extends React.Component { // eslint-disable-line rea
<PluginLeftMenuLink <PluginLeftMenuLink
key={index} key={index}
link={item} link={item}
environments={this.props.environments}
/> />
)); ));
@ -34,6 +35,7 @@ class PluginLeftMenuSection extends React.Component { // eslint-disable-line rea
} }
PluginLeftMenuSection.propTypes = { PluginLeftMenuSection.propTypes = {
environments: React.PropTypes.array,
section: React.PropTypes.object.isRequired, section: React.PropTypes.object.isRequired,
}; };

View File

@ -6,7 +6,9 @@
import { import {
MENU_FETCH, MENU_FETCH,
ENVIRONMENTS_FETCH,
MENU_FETCH_SUCCEEDED, MENU_FETCH_SUCCEEDED,
ENVIRONMENTS_FETCH_SUCCEEDED,
} from './constants'; } from './constants';
@ -22,3 +24,16 @@ export function fetchMenuSucceeded(menu) {
menu, menu,
}; };
} }
export function environmentsFetch() {
return {
type: ENVIRONMENTS_FETCH,
};
}
export function environmentsFetchSucceeded(environments) {
return {
type: ENVIRONMENTS_FETCH_SUCCEEDED,
environments,
};
}

View File

@ -5,4 +5,6 @@
*/ */
export const MENU_FETCH = 'SettingsManager/App/MENU_FETCH'; export const MENU_FETCH = 'SettingsManager/App/MENU_FETCH';
export const ENVIRONMENTS_FETCH = 'SettingsManager/App/ENVIRONMENTS_FETCH';
export const MENU_FETCH_SUCCEEDED = 'SettingsManager/App/MENU_FETCH_SUCCEEDED'; export const MENU_FETCH_SUCCEEDED = 'SettingsManager/App/MENU_FETCH_SUCCEEDED';
export const ENVIRONMENTS_FETCH_SUCCEEDED = 'SettingsManager/App/ENVIRONMENTS_FETCH_SUCCEEDED';

View File

@ -15,8 +15,8 @@ import PluginLeftMenu from 'components/PluginLeftMenu';
import { define } from 'i18n'; import { define } from 'i18n';
import messages from '../../translations/en.json'; import messages from '../../translations/en.json';
import { menuFetch } from './actions'; import { menuFetch, environmentsFetch } from './actions';
import { makeSelectSections } from './selectors'; import { makeSelectSections, makeSelectEnvironments } from './selectors';
import styles from './styles.scss'; import styles from './styles.scss';
define(map(messages, (message, id) => ({ define(map(messages, (message, id) => ({
id, id,
@ -27,12 +27,13 @@ define(map(messages, (message, id) => ({
class App extends React.Component { class App extends React.Component {
componentDidMount() { componentDidMount() {
this.props.menuFetch(); this.props.menuFetch();
this.props.environmentsFetch();
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
// redirect the user to the first general section // redirect the user to the first general section
if (!this.props.params.slug && !isEmpty(nextProps.sections)) { if (!this.props.params.slug && !isEmpty(nextProps.sections)) {
this.props.history.push(`${this.props.location.pathname}/${nextProps.sections[0].items[0].slug}`) this.props.history.push(`${this.props.location.pathname}/${nextProps.sections[0].items[0].slug}`);
} }
} }
@ -51,7 +52,7 @@ class App extends React.Component {
*/} */}
<div className={`container-fluid ${styles.noPadding}`}> <div className={`container-fluid ${styles.noPadding}`}>
<div className="row"> <div className="row">
<PluginLeftMenu sections={this.props.sections} /> <PluginLeftMenu sections={this.props.sections} environments={this.props.environments} />
{React.Children.toArray(content)} {React.Children.toArray(content)}
</div> </div>
</div> </div>
@ -66,6 +67,8 @@ App.contextTypes = {
App.propTypes = { App.propTypes = {
children: React.PropTypes.node.isRequired, children: React.PropTypes.node.isRequired,
environments: React.PropTypes.array,
environmentsFetch: React.PropTypes.func,
exposedComponents: React.PropTypes.object.isRequired, exposedComponents: React.PropTypes.object.isRequired,
history: React.PropTypes.object, history: React.PropTypes.object,
location: React.PropTypes.object, location: React.PropTypes.object,
@ -78,6 +81,7 @@ export function mapDispatchToProps(dispatch) {
return bindActionCreators( return bindActionCreators(
{ {
menuFetch, menuFetch,
environmentsFetch,
}, },
dispatch dispatch
); );
@ -85,6 +89,7 @@ export function mapDispatchToProps(dispatch) {
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
sections: makeSelectSections(), sections: makeSelectSections(),
environments: makeSelectEnvironments(),
}); });
// Wrap the component to inject dispatch and state into it // Wrap the component to inject dispatch and state into it

View File

@ -7,16 +7,22 @@
import { fromJS, List } from 'immutable'; import { fromJS, List } from 'immutable';
import { import {
MENU_FETCH_SUCCEEDED, MENU_FETCH_SUCCEEDED,
ENVIRONMENTS_FETCH_SUCCEEDED,
} from './constants'; } from './constants';
/* eslint-disable new-cap */
const initialState = fromJS({ const initialState = fromJS({
sections: List(), // eslint-disable-line new-cap sections: List(), // eslint-disable-line new-cap
environments: List(),
}); });
function appReducer(state = initialState, action) { function appReducer(state = initialState, action) {
switch (action.type) { switch (action.type) {
case MENU_FETCH_SUCCEEDED: case MENU_FETCH_SUCCEEDED:
return state.set('sections', List(action.menu.sections)); // eslint-disable-line new-cap return state.set('sections', List(action.menu.sections));
case ENVIRONMENTS_FETCH_SUCCEEDED:
return state
.set('environments', List(action.environments.environments));
default: default:
return state; return state;
} }

View File

@ -1,8 +1,8 @@
import { takeLatest } from 'redux-saga'; import { takeLatest } from 'redux-saga';
import { put, fork } from 'redux-saga/effects'; import { put, fork, take, cancel } from 'redux-saga/effects';
import { fetchMenuSucceeded } from './actions'; import { fetchMenuSucceeded, environmentsFetchSucceeded } from './actions';
import { MENU_FETCH } from './constants'; import { MENU_FETCH, MENU_FETCH_SUCCEEDED, ENVIRONMENTS_FETCH, ENVIRONMENTS_FETCH_SUCCEEDED } from './constants';
export function* fetchMenu() { export function* fetchMenu() {
try { try {
@ -21,9 +21,31 @@ export function* fetchMenu() {
} }
} }
export function* fetchEnvironments() {
try {
const opts = {
method: 'GET',
};
const response = yield fetch('/settings-manager/environments', opts);
const data = yield response.json();
yield put(environmentsFetchSucceeded(data));
} catch(error) {
console.log(error);
}
}
function* defaultSaga() { function* defaultSaga() {
yield fork(takeLatest, MENU_FETCH, fetchMenu); const loadMenu = yield fork(takeLatest, MENU_FETCH, fetchMenu);
const loadEnvironments = yield fork(takeLatest, ENVIRONMENTS_FETCH, fetchEnvironments);
yield take(MENU_FETCH_SUCCEEDED);
yield cancel(loadMenu);
yield take(ENVIRONMENTS_FETCH_SUCCEEDED);
yield cancel(loadEnvironments)
} }
export default [defaultSaga]; export default [defaultSaga];

View File

@ -27,5 +27,10 @@ const makeSelectSections = () => createSelector(
(globalSate) => globalSate.get('sections').toJS(), (globalSate) => globalSate.get('sections').toJS(),
); );
export { selectLocationState, makeSelectSections }; const makeSelectEnvironments = () => createSelector(
selectGlobalDomain(),
(globalSate) => globalSate.get('environments').toJS(),
);
export { selectLocationState, makeSelectSections, makeSelectEnvironments };
export default selectGlobalDomain; export default selectGlobalDomain;

View File

@ -1,14 +1,13 @@
/* /*
* *
* Home actions * Home actions
* *
*/ */
import { forEach } from 'lodash';
import { forEach } from 'lodash';
import { import {
CONFIG_FETCH, CONFIG_FETCH,
ENVIRONMENTS_FETCH,
CONFIG_FETCH_SUCCEEDED, CONFIG_FETCH_SUCCEEDED,
ENVIRONMENTS_FETCH_SUCCEEDED,
DEFAULT_ACTION, DEFAULT_ACTION,
} from './constants'; } from './constants';
@ -25,19 +24,6 @@ export function configFetch(endPoint) {
}; };
} }
export function environmentsFetch() {
return {
type: ENVIRONMENTS_FETCH,
};
}
export function environmentsFetchSucceeded(environments) {
return {
type: ENVIRONMENTS_FETCH_SUCCEEDED,
environments,
};
}
export function configFetchSucceded(configs) { export function configFetchSucceded(configs) {
const data = {}; const data = {};
forEach(configs.sections, (section) => { forEach(configs.sections, (section) => {

View File

@ -5,7 +5,5 @@
*/ */
export const CONFIG_FETCH = 'src/Home/CONFIG_FETCH'; export const CONFIG_FETCH = 'src/Home/CONFIG_FETCH';
export const ENVIRONMENTS_FETCH = 'src/Home/ENVIRONMENTS_FETCH';
export const CONFIG_FETCH_SUCCEEDED = 'src/Home/CONFIG_FETCH_SUCCEEDED'; export const CONFIG_FETCH_SUCCEEDED = 'src/Home/CONFIG_FETCH_SUCCEEDED';
export const ENVIRONMENTS_FETCH_SUCCEEDED = 'src/Home/ENVIRONMENTS_FETCH_SUCCEEDED';
export const DEFAULT_ACTION = 'src/Home/DEFAULT_ACTION'; export const DEFAULT_ACTION = 'src/Home/DEFAULT_ACTION';

View File

@ -7,49 +7,31 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { includes } from 'lodash';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import selectHome from './selectors'; import selectHome from './selectors';
import { configFetch, environmentsFetch } from './actions' import { configFetch } from './actions'
import styles from './styles.scss'; import styles from './styles.scss';
import config from './config.json';
export class Home extends React.Component { // eslint-disable-line react/prefer-stateless-function export class Home extends React.Component { // eslint-disable-line react/prefer-stateless-function
componentDidMount() { componentDidMount() {
// always fetch environments
this.props.environmentsFetch();
if (this.props.params.slug) { if (this.props.params.slug) {
const isEnvironmentsRequired = includes(config.environmentsRequired, this.props.params.slug); const apiUrl = this.props.params.env ? `${this.props.params.slug}/${this.props.params.env}` : this.props.params.slug;
this.props.configFetch(apiUrl);
if (!isEnvironmentsRequired) {
this.props.configFetch(this.props.params.slug);
} else if (this.props.params.env){
this.props.configFetch(`${this.props.params.slug}/${this.props.params.env}`);
}
} }
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const isEnvironmentsRequired = nextProps.params.slug ? includes(config.environmentsRequired, nextProps.params.slug) : false;
// check if params slug updated // check if params slug updated
if (this.props.params.slug !== nextProps.params.slug && nextProps.params.slug) { if (this.props.params.slug !== nextProps.params.slug && nextProps.params.slug) {
// redirect user if environnemnt is required and params environment not provided
if (isEnvironmentsRequired && !nextProps.params.env) {
this.props.history.push(`${nextProps.location.pathname}/${nextProps.environments[0].name}`)
}
// get data from api if params slug updated // get data from api if params slug updated
const apiUrl = isEnvironmentsRequired ? `${nextProps.params.slug}/${nextProps.environments[0].name}` : nextProps.params.slug; const apiUrl = nextProps.params.env ? `${nextProps.params.slug}/${nextProps.params.env}` : nextProps.params.slug;
this.props.configFetch(apiUrl); this.props.configFetch(apiUrl);
} else if (this.props.params.env !== nextProps.params.env) { } else if (this.props.params.env !== nextProps.params.env) {
console.log('-------');
// get data if params env updated // get data if params env updated
this.props.configFetch(`${this.props.params.slug}/${nextProps.params.env}`); this.props.configFetch(`${this.props.params.slug}/${nextProps.params.env}`);
} }
@ -75,7 +57,6 @@ function mapDispatchToProps(dispatch) {
return bindActionCreators( return bindActionCreators(
{ {
configFetch, configFetch,
environmentsFetch,
}, },
dispatch dispatch
) )
@ -83,8 +64,6 @@ function mapDispatchToProps(dispatch) {
Home.propTypes = { Home.propTypes = {
configFetch: React.PropTypes.func.isRequired, configFetch: React.PropTypes.func.isRequired,
environmentsFetch: React.PropTypes.func.isRequired,
history: React.PropTypes.object.isRequired,
params: React.PropTypes.object.isRequired, params: React.PropTypes.object.isRequired,
}; };

View File

@ -4,12 +4,10 @@
* *
*/ */
import { fromJS, List, Map, OrderedMap } from 'immutable'; import { fromJS, Map, OrderedMap } from 'immutable';
import { import {
CONFIG_FETCH, CONFIG_FETCH,
ENVIRONMENTS_FETCH,
CONFIG_FETCH_SUCCEEDED, CONFIG_FETCH_SUCCEEDED,
ENVIRONMENTS_FETCH_SUCCEEDED,
} from './constants'; } from './constants';
/* eslint-disable new-cap */ /* eslint-disable new-cap */
@ -18,7 +16,6 @@ const initialState = fromJS({
configsDisplay: OrderedMap(), configsDisplay: OrderedMap(),
initialData: Map(), initialData: Map(),
modifiedData: Map(), modifiedData: Map(),
environments: List(),
}); });
function homeReducer(state = initialState, action) { function homeReducer(state = initialState, action) {
@ -31,12 +28,6 @@ function homeReducer(state = initialState, action) {
.set('configsDisplay', OrderedMap(action.configs)) .set('configsDisplay', OrderedMap(action.configs))
.set('initialData', Map(action.data)) .set('initialData', Map(action.data))
.set('modifiedData', Map(action.data)); .set('modifiedData', Map(action.data));
case ENVIRONMENTS_FETCH:
return state.set('loading', true);
case ENVIRONMENTS_FETCH_SUCCEEDED:
return state
.set('loading', false)
.set('environments', List(action.environments.environments));
default: default:
return state; return state;
} }

View File

@ -1,8 +1,8 @@
import { takeLatest } from 'redux-saga'; import { takeLatest } from 'redux-saga';
import { take, put, fork, cancel } from 'redux-saga/effects'; import { take, put, fork, cancel } from 'redux-saga/effects';
// import { FormattedMessage } from 'react-intl'; // import { FormattedMessage } from 'react-intl';
import { CONFIG_FETCH, ENVIRONMENTS_FETCH, CONFIG_FETCH_SUCCEEDED, ENVIRONMENTS_FETCH_SUCCEEDED } from './constants'; import { CONFIG_FETCH, CONFIG_FETCH_SUCCEEDED } from './constants';
import { configFetchSucceded, environmentsFetchSucceeded } from './actions'; import { configFetchSucceded } from './actions';
export function* fetchConfig(action) { export function* fetchConfig(action) {
try { try {
@ -22,31 +22,11 @@ export function* fetchConfig(action) {
} }
} }
export function* fetchEnvironments() {
try {
const opts = {
method: 'GET',
};
const response = yield fetch('/settings-manager/environments', opts);
const data = yield response.json();
yield put(environmentsFetchSucceeded(data));
} catch(error) {
console.log(error);
}
}
// Individual exports for testing // Individual exports for testing
export function* defaultSaga() { export function* defaultSaga() {
const loadConfig = yield fork(takeLatest, CONFIG_FETCH, fetchConfig); const loadConfig = yield fork(takeLatest, CONFIG_FETCH, fetchConfig);
const loadEnvironments = yield fork(takeLatest, ENVIRONMENTS_FETCH, fetchEnvironments);
yield take(CONFIG_FETCH_SUCCEEDED); yield take(CONFIG_FETCH_SUCCEEDED);
yield cancel(loadConfig); yield cancel(loadConfig);
yield take(ENVIRONMENTS_FETCH_SUCCEEDED);
yield cancel(loadEnvironments);
} }
// All sagas to be loaded // All sagas to be loaded