diff --git a/frontend/package.json b/frontend/package.json
index b94ee9ac..41c2703f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -21,8 +21,10 @@
"dependencies": {
"constant-case": "^2.0.0",
"express": "^4.14.0",
+ "force-array": "^3.1.0",
"inherits": "^2.0.3",
"locale": "^0.1.0",
+ "lodash.find": "^4.6.0",
"lodash.isempty": "^4.4.0",
"lodash.template": "^4.4.0",
"lodash.uniq": "^4.5.0",
@@ -43,6 +45,7 @@
"redux-logger": "^2.7.4",
"redux-promise-middleware": "^4.2.0",
"redux-thunk": "^2.1.0",
+ "reselect": "^2.5.4",
"st": "^1.2.0",
"styled-components": "^1.1.3",
"url-loader": "^0.5.7"
diff --git a/frontend/src/containers/dashboard/index.js b/frontend/src/containers/dashboard/index.js
deleted file mode 100644
index 9c4c7f2b..00000000
--- a/frontend/src/containers/dashboard/index.js
+++ /dev/null
@@ -1,72 +0,0 @@
-const React = require('react');
-const ReactIntl = require('react-intl');
-const ReactRedux = require('react-redux');
-const ReactRouter = require('react-router');
-
-const H1 = require('@ui/components/h1');
-const Li = require('@ui/components/horizontal-list/li');
-const Redirect = require('@components/redirect');
-const Ul = require('@ui/components/horizontal-list/ul');
-
-const Projects = require('./projects');
-const Settings = require('./settings');
-
-const {
- FormattedMessage
-} = ReactIntl;
-
-const {
- connect
-} = ReactRedux;
-
-const {
- Link,
- Match,
- Miss
-} = ReactRouter;
-
-const Dashboard = ({
- pathname = '',
- user = {}
-}) => {
- return (
-
- );
-};
-
-Dashboard.propTypes = {
- pathname: React.PropTypes.string,
- user: React.PropTypes.shape({
- id: React.PropTypes.string,
- name: React.PropTypes.string
- })
-};
-
-const mapStateToProps = (state) => ({
- projects: state.session.data.projects,
- user: {
- id: state.session.data.name,
- name: state.session.data.name
- }
-});
-
-module.exports = connect(mapStateToProps)(Dashboard);
diff --git a/frontend/src/containers/dashboard/projects.js b/frontend/src/containers/dashboard/projects.js
deleted file mode 100644
index 033bd8b8..00000000
--- a/frontend/src/containers/dashboard/projects.js
+++ /dev/null
@@ -1,13 +0,0 @@
-const ReactRedux = require('react-redux');
-
-const Projects = require('@components/projects');
-
-const {
- connect
-} = ReactRedux;
-
-const mapStateToProps = (state) => ({
- projects: state.session.data.projects
-});
-
-module.exports = connect(mapStateToProps)(Projects);
diff --git a/frontend/src/containers/dashboard/settings.js b/frontend/src/containers/dashboard/settings.js
deleted file mode 100644
index 058aad75..00000000
--- a/frontend/src/containers/dashboard/settings.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const React = require('react');
-const ReactIntl = require('react-intl');
-const ReactRedux = require('react-redux');
-// const ReactRouter = require('react-router');
-//
-const Column = require('@ui/components/column');
-// const Button = require('@ui/components/button');
-const Row = require('@ui/components/row');
-
-const {
- FormattedMessage
-} = ReactIntl;
-
-const {
- connect
-} = ReactRedux;
-
-// const {
-// Link,
-// Match,
-// Miss,
-// Redirect
-// } = ReactRouter;
-
-const Settings = () => {
- return (
-
-
-
-
-
- );
-};
-
-Settings.propTypes = {
- Settings: React.PropTypes.array
-};
-
-const mapStateToProps = (state) => ({
- Settings: state.session.data.Settings
-});
-
-module.exports = connect(mapStateToProps)(Settings);
diff --git a/frontend/src/containers/home/index.js b/frontend/src/containers/home/index.js
index 266cbef8..0ea7e3eb 100644
--- a/frontend/src/containers/home/index.js
+++ b/frontend/src/containers/home/index.js
@@ -1,19 +1,16 @@
const React = require('react');
-const ReactIntl = require('react-intl');
const ReactRedux = require('react-redux');
const ReactRouter = require('react-router');
const Styled = require('styled-components');
+const selectors = require('@state/selectors');
+
const Container = require('@ui/components/container');
-const Dashboard = require('@containers/dashboard');
const Li = require('@ui/components/horizontal-list/li');
const Org = require('@containers/org');
const Redirect = require('@components/redirect');
const Ul = require('@ui/components/horizontal-list/ul');
-
-const {
- FormattedMessage
-} = ReactIntl;
+const NotFound = require('@containers/not-found');
const {
connect
@@ -29,14 +26,16 @@ const {
default: styled
} = Styled;
+const {
+ orgsSelector
+} = selectors;
+
const StyledNav = styled.div`
background-color: #f2f2f2;
`;
const Home = ({
- orgs = [],
- pathname = '/',
- user = {}
+ orgs = []
}) => {
const navLinks = orgs.map(({
id,
@@ -53,47 +52,40 @@ const Home = ({
);
});
+ const notFound = !orgs.length
+ ?
+ : Redirect(`/${orgs[0].id}`);
+
return (
);
};
Home.propTypes = {
- orgs: React.PropTypes.arrayOf(React.PropTypes.shape({
- id: React.PropTypes.string,
- name: React.PropTypes.string
- })),
- pathname: React.PropTypes.string,
- user: React.PropTypes.shape({
- id: React.PropTypes.string,
- name: React.PropTypes.string
- })
+ orgs: React.PropTypes.arrayOf(
+ React.PropTypes.shape({
+ owner: React.PropTypes.string,
+ uuid: React.PropTypes.string,
+ id: React.PropTypes.string,
+ name: React.PropTypes.string
+ })
+ )
};
const mapStateToProps = (state) => ({
- orgs: state.session.data.orgs,
- user: {
- id: state.session.data.name,
- name: state.session.data.name
- }
+ orgs: orgsSelector(state)
});
module.exports = connect(mapStateToProps)(Home);
diff --git a/frontend/src/containers/org/index.js b/frontend/src/containers/org/index.js
index ebdeded3..588a5b4b 100644
--- a/frontend/src/containers/org/index.js
+++ b/frontend/src/containers/org/index.js
@@ -4,15 +4,19 @@ const ReactIntl = require('react-intl');
const ReactRedux = require('react-redux');
const ReactRouter = require('react-router');
+const selectors = require('@state/selectors');
+
const H1 = require('@ui/components/h1');
const Li = require('@ui/components/horizontal-list/li');
const Ul = require('@ui/components/horizontal-list/ul');
-
const NotFound = require('@containers/not-found');
+const Redirect = require('@components/redirect');
-const People = require('./people');
-const Projects = require('./projects');
-const Settings = require('./settings');
+const SectionComponents = {
+ people: require('./people'),
+ projects: require('./projects'),
+ settings: require('./settings'),
+};
const {
FormattedMessage
@@ -25,75 +29,71 @@ const {
const {
Link,
Match,
- Miss,
- Redirect
+ Miss
} = ReactRouter;
+const {
+ orgByIdSelector,
+ orgSectionsSelector
+} = selectors;
+
const Org = ({
org = {},
- params = {},
- user = {}
+ sections = []
}) => {
- if (user.id === params.org) {
- return null;
- }
-
if (isEmpty(org)) {
return (
);
}
+ const navLinks = sections.map((name) => (
+
+
+
+
+
+ ));
+
+ const navMatches = sections.map((name) => (
+
+ ));
+
+ const missMatch = !sections.length ? null : (
+
+ );
+
return (
{org.name}
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
+ {navLinks}
-
-
-
-
+ {navMatches}
+ {missMatch}
);
};
Org.propTypes = {
org: React.PropTypes.shape({
+ owner: React.PropTypes.string,
+ uuid: React.PropTypes.string,
id: React.PropTypes.string,
name: React.PropTypes.string
}),
- params: React.PropTypes.shape({
- org: React.PropTypes.string
- }),
- user: React.PropTypes.shape({
- id: React.PropTypes.string,
- name: React.PropTypes.string
- })
+ sections: React.PropTypes.arrayOf(
+ React.PropTypes.string
+ )
};
const mapStateToProps = (state, ownProps) => ({
- org: state.session.data.orgs.filter((org) => {
- return org.id === ownProps.params.org;
- }).pop(),
- user: {
- id: state.session.data.name,
- name: state.session.data.name
- }
+ org: orgByIdSelector(ownProps.params.org)(state),
+ sections: orgSectionsSelector(ownProps.params.org)(state)
});
module.exports = connect(mapStateToProps)(Org);
diff --git a/frontend/src/containers/org/projects.js b/frontend/src/containers/org/projects.js
index f26adbd9..77e4b431 100644
--- a/frontend/src/containers/org/projects.js
+++ b/frontend/src/containers/org/projects.js
@@ -1,17 +1,18 @@
-const ReactRedux = require('react-redux');
-
const Projects = require('@components/projects');
+const ReactRedux = require('react-redux');
+const selectors = require('@state/selectors');
const {
connect
} = ReactRedux;
-const mapStateToProps = (state, ownProps) => {
- return {
- projects: (state.session.data.orgs.filter((org) => {
- return org.id === ownProps.params.org;
- }).pop() || {}).projects
- };
-};
+const {
+ projectsByOrgIdSelector
+} = selectors;
+
+
+const mapStateToProps = (state, ownProps) => ({
+ projects: projectsByOrgIdSelector(ownProps.params.org)(state)
+});
module.exports = connect(mapStateToProps)(Projects);
diff --git a/frontend/src/root.js b/frontend/src/root.js
index b31938d5..3d7f17fa 100644
--- a/frontend/src/root.js
+++ b/frontend/src/root.js
@@ -24,32 +24,57 @@ const {
} = ReactRouter;
const store = Store({
- session: {
- data: {
+ projects: {
+ data: [{
+ uuid: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
+ org: 'e12ad7db-91b2-4154-83dd-40dcfc700dcc',
+ id: 'forest-foundation-dev',
+ name: 'Forest Foundation Dev',
+ plan: '20.05$ per day'
+ }, {
+ uuid: '9fcb374d-a267-4c2a-9d9c-ba469b804639',
+ org: 'e12ad7db-91b2-4154-83dd-40dcfc700dcc',
+ id: 'forest-foundation-testing',
+ name: 'Forest Foundation Testing',
+ plan: '20.05$ per day'
+ }, {
+ uuid: 'ac2c2498-e865-4ee3-9e26-8c75a81cbe25',
+ org: 'e12ad7db-91b2-4154-83dd-40dcfc700dcc',
+ id: 'forest-foundation-production',
+ name: 'Forest Foundation Production',
+ plan: '100.17$ per day'
+ }]
+ },
+ orgs: {
+ ui: {
+ sections: [
+ 'projects',
+ 'people',
+ 'settings'
+ ]
+ },
+ data: [{
+ hide: ['people'],
+ owner: 'b94033c1-3665-4c36-afab-d9c3d0b51c01',
id: 'nicola',
- name: 'Nicola',
- projects: [],
- orgs: [{
- id: 'biz-tech',
- name: 'BizTech',
- pinned: true,
- projects: [{
- id: 'forest-foundation-dev',
- name: 'Forest Foundation Dev',
- plan: '20.05$ per day'
- }, {
- id: 'forest-foundation-testing',
- name: 'Forest Foundation Testing',
- plan: '20.05$ per day'
- }, {
- id: 'forest-foundation-production',
- name: 'Forest Foundation Production',
- plan: '100.17$ per day'
- }],
- }, {
- id: 'make-us-proud',
- name: 'Make Us Proud',
- }]
+ name: 'Your Dashboard'
+ }, {
+ owner: 'b94033c1-3665-4c36-afab-d9c3d0b51c01',
+ uuid: 'e12ad7db-91b2-4154-83dd-40dcfc700dcc',
+ id: 'biz-tech',
+ name: 'BizTech'
+ }, {
+ owner: 'b94033c1-3665-4c36-afab-d9c3d0b51c01',
+ uuid: '551f316d-e414-480f-9787-b4c408db3edd',
+ id: 'make-us-proud',
+ name: 'Make Us Proud',
+ }]
+ },
+ account: {
+ data: {
+ uuid: 'b94033c1-3665-4c36-afab-d9c3d0b51c01',
+ id: 'nicola',
+ name: 'Nicola'
}
}
});
diff --git a/frontend/src/state/reducers/session.js b/frontend/src/state/reducers/account.js
similarity index 100%
rename from frontend/src/state/reducers/session.js
rename to frontend/src/state/reducers/account.js
diff --git a/frontend/src/state/reducers/index.js b/frontend/src/state/reducers/index.js
index deb52fc5..05960668 100644
--- a/frontend/src/state/reducers/index.js
+++ b/frontend/src/state/reducers/index.js
@@ -6,8 +6,10 @@ const {
module.exports = () => {
return combineReducers({
+ account: require('@state/reducers/account'),
app: require('@state/reducers/app'),
intl: require('@state/reducers/intl'),
- session: require('@state/reducers/session')
+ orgs: require('@state/reducers/orgs'),
+ projects: require('@state/reducers/projects')
});
};
diff --git a/frontend/src/state/reducers/orgs.js b/frontend/src/state/reducers/orgs.js
new file mode 100644
index 00000000..7fec06fb
--- /dev/null
+++ b/frontend/src/state/reducers/orgs.js
@@ -0,0 +1,9 @@
+const ReduxActions = require('redux-actions');
+
+const {
+ handleActions
+} = ReduxActions;
+
+module.exports = handleActions({
+ 'x': (state) => state // somehow handleActions needs at least one reducer
+}, {});
diff --git a/frontend/src/state/reducers/projects.js b/frontend/src/state/reducers/projects.js
new file mode 100644
index 00000000..7fec06fb
--- /dev/null
+++ b/frontend/src/state/reducers/projects.js
@@ -0,0 +1,9 @@
+const ReduxActions = require('redux-actions');
+
+const {
+ handleActions
+} = ReduxActions;
+
+module.exports = handleActions({
+ 'x': (state) => state // somehow handleActions needs at least one reducer
+}, {});
diff --git a/frontend/src/state/selectors.js b/frontend/src/state/selectors.js
new file mode 100644
index 00000000..a71cf761
--- /dev/null
+++ b/frontend/src/state/selectors.js
@@ -0,0 +1,37 @@
+const find = require('lodash.find');
+const forceArray = require('force-array');
+const reselect = require('reselect');
+
+const {
+ createSelector
+} = reselect;
+
+const account = (state) => state.account.data;
+const orgUiSections = (state) => state.orgs.ui.sections;
+const orgs = (state) => state.orgs.data;
+const projects = (state) => state.projects.data;
+
+const orgById = (id) => createSelector(
+ orgs,
+ (orgs) => find(orgs, ['id', id])
+);
+
+const projectsByOrgId = (orgId) => createSelector(
+ [projects, orgById(orgId)],
+ (projects, org) => projects.filter((p) => p.org === org.uuid)
+);
+
+const orgSections = (orgId) => createSelector(
+ [orgById(orgId), orgUiSections],
+ (org, sections) => sections.filter(
+ (section) => forceArray(org.hide).indexOf(section) < 0
+ )
+);
+
+module.exports = {
+ accountSelector: account,
+ orgByIdSelector: orgById,
+ orgsSelector: orgs,
+ orgSectionsSelector: orgSections,
+ projectsByOrgIdSelector: projectsByOrgId
+};
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 1c996fe3..caac1c72 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -2480,6 +2480,12 @@ for-own@^0.1.4:
dependencies:
for-in "^0.1.5"
+force-array:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/force-array/-/force-array-3.1.0.tgz#a060f6d4188dc7daa6fe562df39aeaabca404784"
+ dependencies:
+ is-array "^1.0.1"
+
foreach@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
@@ -2961,6 +2967,10 @@ irregular-plurals@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.2.0.tgz#38f299834ba8c00c30be9c554e137269752ff3ac"
+is-array@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-array/-/is-array-1.0.1.tgz#e9850cc2cc860c3bc0977e84ccf0dd464584279a"
+
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -3434,6 +3444,10 @@ lodash.filter@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
+lodash.find:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
+
lodash.flatten@^4.2.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
@@ -4742,6 +4756,10 @@ requires-port@1.0.x, requires-port@1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+reselect:
+ version "2.5.4"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-2.5.4.tgz#b7d23fdf00b83fa7ad0279546f8dbbbd765c7047"
+
resolve-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-1.0.0.tgz#4eaeea41ed040d1702457df64a42b2b07d246f9f"