link service

This commit is contained in:
Sérgio Ramos 2017-01-02 22:32:29 +00:00
parent af4d26b5a5
commit 355c7d59d9
26 changed files with 470 additions and 107 deletions

View File

@ -3,15 +3,16 @@ const ReactRedux = require('react-redux');
const ReactRouter = require('react-router'); const ReactRouter = require('react-router');
const Styled = require('styled-components'); const Styled = require('styled-components');
const selectors = require('@state/selectors');
const Container = require('@ui/components/container'); const Container = require('@ui/components/container');
const Li = require('@ui/components/horizontal-list/li'); const Li = require('@ui/components/horizontal-list/li');
const Org = require('@containers/org'); const Org = require('@containers/org');
const PropTypes = require('@root/prop-types');
const Redirect = require('@components/redirect'); const Redirect = require('@components/redirect');
const selectors = require('@state/selectors');
const Ul = require('@ui/components/horizontal-list/ul'); const Ul = require('@ui/components/horizontal-list/ul');
const NotFound = require('@containers/not-found'); const NotFound = require('@containers/not-found');
const { const {
connect connect
} = ReactRedux; } = ReactRedux;
@ -74,14 +75,7 @@ const Home = ({
}; };
Home.propTypes = { Home.propTypes = {
orgs: React.PropTypes.arrayOf( orgs: React.PropTypes.arrayOf(PropTypes.org)
React.PropTypes.shape({
owner: React.PropTypes.string,
uuid: React.PropTypes.string,
id: React.PropTypes.string,
name: React.PropTypes.string
})
)
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({

View File

@ -4,6 +4,7 @@ const ReactRedux = require('react-redux');
const ReactRouter = require('react-router'); const ReactRouter = require('react-router');
const NotFound = require('@containers/not-found'); const NotFound = require('@containers/not-found');
const PropTypes = require('@root/prop-types');
const Redirect = require('@components/redirect'); const Redirect = require('@components/redirect');
const selectors = require('@state/selectors'); const selectors = require('@state/selectors');
@ -58,15 +59,8 @@ const Org = ({
}; };
Org.propTypes = { Org.propTypes = {
org: React.PropTypes.shape({ org: PropTypes.org,
owner: React.PropTypes.string, sections: PropTypes.sections
uuid: React.PropTypes.string,
id: React.PropTypes.string,
name: React.PropTypes.string
}),
sections: React.PropTypes.arrayOf(
React.PropTypes.string
)
}; };
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({

View File

@ -1,6 +1,7 @@
const React = require('react'); const React = require('react');
const ReactRedux = require('react-redux'); const ReactRedux = require('react-redux');
const PropTypes = require('@root/prop-types');
const selectors = require('@state/selectors'); const selectors = require('@state/selectors');
const Section = require('@components/section'); const Section = require('@components/section');
@ -32,13 +33,8 @@ const OrgSection = ({
OrgSection.propTypes = { OrgSection.propTypes = {
children: React.PropTypes.node, children: React.PropTypes.node,
org: React.PropTypes.shape({ org: PropTypes.org,
id: React.PropTypes.string, sections: PropTypes.sections
name: React.PropTypes.string
}),
sections: React.PropTypes.arrayOf(
React.PropTypes.string
)
}; };
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({
@ -46,4 +42,6 @@ const mapStateToProps = (state, ownProps) => ({
sections: orgSectionsSelector(ownProps.params.org)(state) sections: orgSectionsSelector(ownProps.params.org)(state)
}); });
module.exports = connect(mapStateToProps)(OrgSection); module.exports = connect(
mapStateToProps
)(OrgSection);

View File

@ -2,8 +2,8 @@ const React = require('react');
const ReactRedux = require('react-redux'); const ReactRedux = require('react-redux');
const ReactRouter = require('react-router'); const ReactRouter = require('react-router');
const PropTypes = require('@root/prop-types');
const Redirect = require('@components/redirect'); const Redirect = require('@components/redirect');
const Section = require('@components/section');
const selectors = require('@state/selectors'); const selectors = require('@state/selectors');
const SectionComponents = { const SectionComponents = {
@ -34,67 +34,30 @@ const Project = ({
project = {}, project = {},
sections = [] sections = []
}) => { }) => {
const pathname = (props) => ( const navMatches = sections.map((name) => (
`/${props.org}/projects/${props.project}/${props.section}`
);
const name = `${org.name} / ${project.name}`;
const links = sections.map((name) => ({
pathname: pathname({
org: org.id,
project: project.id,
section: name
}),
name
}));
const navMatches = sections.map((name) => {
const pattern = pathname({
org: org.id,
project: project.id,
section: name
});
return (
<Match <Match
component={SectionComponents[name]} component={SectionComponents[name]}
key={name} key={name}
pattern={pattern} pattern={`/:org/projects/:projectId/${name}`}
/> />
); ));
});
const missPathname = pathname({
org: org.id,
project: project.id,
section: sections[0]
});
const missMatch = !sections.length ? null : ( const missMatch = !sections.length ? null : (
<Miss component={Redirect(missPathname)} /> <Miss component={Redirect(`/${org.id}/projects/${project.id}/services`)} />
); );
return ( return (
<Section links={links} name={name}> <div>
{navMatches} {navMatches}
{missMatch} {missMatch}
</Section> </div>
); );
}; };
Project.propTypes = { Project.propTypes = {
org: React.PropTypes.shape({ org: PropTypes.org,
id: React.PropTypes.string, project: PropTypes.project,
name: React.PropTypes.string sections: PropTypes.sections
}),
project: React.PropTypes.shape({
id: React.PropTypes.string,
name: React.PropTypes.string
}),
sections: React.PropTypes.arrayOf(
React.PropTypes.string
)
}; };
const mapStateToProps = (state, { const mapStateToProps = (state, {

View File

@ -1,5 +1,9 @@
const React = require('react'); const React = require('react');
module.exports = () => ( const Section = require('./section');
module.exports = (props) => (
<Section {...props}>
<p>instances</p> <p>instances</p>
</Section>
); );

View File

@ -1,5 +1,9 @@
const React = require('react'); const React = require('react');
module.exports = () => ( const Section = require('./section');
module.exports = (props) => (
<Section {...props}>
<p>manifest</p> <p>manifest</p>
</Section>
); );

View File

@ -1,5 +1,9 @@
const React = require('react'); const React = require('react');
module.exports = () => ( const Section = require('./section');
module.exports = (props) => (
<Section {...props}>
<p>people</p> <p>people</p>
</Section>
); );

View File

@ -0,0 +1,63 @@
const React = require('react');
const ReactRedux = require('react-redux');
const PropTypes = require('@root/prop-types');
const selectors = require('@state/selectors');
const Section = require('@components/section');
const {
connect
} = ReactRedux;
const {
orgByIdSelector,
projectByIdSelector,
projectSectionsSelector
} = selectors;
const OrgSection = ({
children,
org = {},
project = {},
sections = []
}) => {
const name = `${org.name} / ${project.name}`;
const pathname = (props) => (
`/${props.org}/projects/${props.project}/${props.section}`
);
const links = sections.map((name) => ({
pathname: pathname({
org: org.id,
project: project.id,
section: name
}),
name
}));
return (
<Section links={links} name={name}>
{children}
</Section>
);
};
OrgSection.propTypes = {
children: React.PropTypes.node,
org: PropTypes.org,
project: PropTypes.project,
sections: PropTypes.sections
};
const mapStateToProps = (state, {
params = {}
}) => ({
org: orgByIdSelector(params.org)(state),
project: projectByIdSelector(params.projectId)(state),
sections: projectSectionsSelector(state)
});
module.exports = connect(
mapStateToProps
)(OrgSection);

View File

@ -1,5 +1,32 @@
const React = require('react'); const React = require('react');
const ReactRouter = require('react-router');
module.exports = () => ( const Section = require('./section');
<p>services</p> const Services = require('@containers/services');
const Service = require('@containers/service');
const {
Match
} = ReactRouter;
module.exports = () => {
const list = (props) => (
<Section {...props}>
<Services {...props} />
</Section>
); );
return (
<div>
<Match
component={list}
exactly
pattern='/:org/projects/:projectId/services'
/>
<Match
component={Service}
pattern='/:org/projects/:projectId/services/:serviceId/:section?'
/>
</div>
);
};

View File

@ -1,5 +1,9 @@
const React = require('react'); const React = require('react');
module.exports = () => ( const Section = require('./section');
module.exports = (props) => (
<Section {...props}>
<p>settings</p> <p>settings</p>
</Section>
); );

View File

@ -5,6 +5,7 @@ const ReactRouter = require('react-router');
const Button = require('@ui/components/button'); const Button = require('@ui/components/button');
const Column = require('@ui/components/column'); const Column = require('@ui/components/column');
const PropTypes = require('@root/prop-types');
const Row = require('@ui/components/row'); const Row = require('@ui/components/row');
const selectors = require('@state/selectors'); const selectors = require('@state/selectors');
@ -67,16 +68,8 @@ const Projects = ({
}; };
Projects.propTypes = { Projects.propTypes = {
org: React.PropTypes.shape({ org: PropTypes.org,
id: React.PropTypes.string, projects: React.PropTypes.arrayOf(PropTypes.project)
name: React.PropTypes.string
}),
projects: React.PropTypes.arrayOf(
React.PropTypes.shape({
id: React.PropTypes.string,
name: React.PropTypes.string
})
)
}; };
const mapStateToProps = (state, { const mapStateToProps = (state, {

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>activity-feed</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>firewall</p>
);

View File

@ -0,0 +1,109 @@
const React = require('react');
const ReactRedux = require('react-redux');
const ReactRouter = require('react-router');
const PropTypes = require('@root/prop-types');
const Redirect = require('@components/redirect');
const Section = require('@components/section');
const selectors = require('@state/selectors');
const SectionComponents = {
summary: require('./summary'),
instances: require('./instances'),
metrics: require('./metrics'),
networks: require('./networks'),
'tags-metadata': require('./tags-metadata'),
'activity-feed': require('./activity-feed'),
'service-manifest': require('./service-manifest'),
firewall: require('./firewall')
};
const {
connect
} = ReactRedux;
const {
Match,
Miss
} = ReactRouter;
const {
orgByIdSelector,
serviceSectionsSelector,
projectByIdSelector,
serviceByIdSelector
} = selectors;
const Service = ({
org = {},
project = {},
sections = [],
service = {}
}) => {
const name = `${org.name} / ${project.name} / ${service.name}`;
const pathname = ({
org,
project,
service,
section
}) => (
`/${org}/projects/${project}/services/${service}/${section}`
);
const links = sections.map((name) => ({
pathname: pathname({
org: org.id,
project: project.id,
service: service.id,
section: name
}),
name
}));
const navMatches = sections.map((name) => (
<Match
component={SectionComponents[name]}
key={name}
pattern={`/:org/projects/:projectId/services/:serviceId/${name}`}
/>
));
const redirectHref = pathname({
org: org.id,
project: project.id,
service: service.id,
section: 'summary'
});
const missMatch = !sections.length ? null : (
<Miss component={Redirect(redirectHref)} />
);
return (
<Section links={links} name={name}>
{navMatches}
{missMatch}
</Section>
);
};
Service.propTypes = {
org: PropTypes.org,
project: PropTypes.project,
sections: PropTypes.sections,
service: PropTypes.service
};
const mapStateToProps = (state, {
params = {}
}) => ({
org: orgByIdSelector(params.org)(state),
project: projectByIdSelector(params.projectId)(state),
sections: serviceSectionsSelector(state),
service: serviceByIdSelector(params.serviceId)(state)
});
module.exports = connect(
mapStateToProps
)(Service);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>instances</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>metrics</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>networks</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>service-manifest</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>summary</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>tags-metadata</p>
);

View File

@ -0,0 +1,95 @@
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 PropTypes = require('@root/prop-types');
const Row = require('@ui/components/row');
const selectors = require('@state/selectors');
const {
connect
} = ReactRedux;
const {
FormattedMessage
} = ReactIntl;
const {
orgByIdSelector,
projectByIdSelector,
servicesByProjectIdSelector
} = selectors;
const {
Link
} = ReactRouter;
const Services = ({
org = {},
project = {},
services = []
}) => {
const empty = services.length ? null : (
<Row>
<Column xs={12}>
<p name='empty'>
<FormattedMessage id='no-personal-projects' />
</p>
</Column>
</Row>
);
const serviceList = (services) => {
if (!services || !services.length) {
return null;
}
const list = services.map((service) => {
const to = `/${org.id}/projects/${project.id}/services/${service.id}`;
return (
<li key={service.id}>
<Link activeClassName='active' to={to}>
{service.name}
</Link>
{serviceList(service.services)}
</li>
);
});
return (
<ul>
{list}
</ul>
);
};
return (
<div>
{empty}
<Row>
{serviceList(services)}
</Row>
</div>
);
};
Services.propTypes = {
org: PropTypes.org,
project: PropTypes.project,
services: React.PropTypes.arrayOf(PropTypes.service)
};
const mapStateToProps = (state, {
params = {}
}) => ({
org: orgByIdSelector(params.org)(state),
project: projectByIdSelector(params.projectId)(state),
services: servicesByProjectIdSelector(params.projectId)(state)
});
module.exports = connect(
mapStateToProps
)(Services);

View File

@ -155,36 +155,44 @@
}, },
"data": [{ "data": [{
"uuid": "081a792c-47e0-4439-924b-2efa9788ae9e", "uuid": "081a792c-47e0-4439-924b-2efa9788ae9e",
"id": "nginx",
"name": "Nginx", "name": "Nginx",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "be227788-74f1-4e5b-a85f-b5c71cbae8d8", "uuid": "be227788-74f1-4e5b-a85f-b5c71cbae8d8",
"id": "wordpress",
"name": "Wordpress", "name": "Wordpress",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "6a0eee76-c019-413b-9d5f-44712b55b993", "uuid": "6a0eee76-c019-413b-9d5f-44712b55b993",
"id": "nfs",
"name": "NFS", "name": "NFS",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "6d31aff4-de1e-4042-a983-fbd23d5c530c", "uuid": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"id": "memcached",
"name": "Memcached", "name": "Memcached",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "4ee4103e-1a52-4099-a48e-01588f597c70", "uuid": "4ee4103e-1a52-4099-a48e-01588f597c70",
"id": "percona",
"name": "Percona", "name": "Percona",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "9572d367-c4ae-4fb1-8ad5-f5e3830e7034", "uuid": "9572d367-c4ae-4fb1-8ad5-f5e3830e7034",
"id": "primary",
"name": "Primary", "name": "Primary",
"parent": "9572d367-c4ae-4fb1-8ad5-f5e3830e7034", "parent": "4ee4103e-1a52-4099-a48e-01588f597c70",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "c8411ef0-ab39-42cb-a704-d20b170eff31", "uuid": "c8411ef0-ab39-42cb-a704-d20b170eff31",
"id": "secondaries",
"name": "Secondaries", "name": "Secondaries",
"parent": "9572d367-c4ae-4fb1-8ad5-f5e3830e7034", "parent": "4ee4103e-1a52-4099-a48e-01588f597c70",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}, { }, {
"uuid": "97c68055-db88-45c9-ad49-f26da4264777", "uuid": "97c68055-db88-45c9-ad49-f26da4264777",
"id": "consul",
"name": "Consul", "name": "Consul",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401" "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
}] }]

View File

@ -0,0 +1,31 @@
const React = require('react');
const BaseObject = {
uuid: React.PropTypes.string,
id: React.PropTypes.string,
name: React.PropTypes.string
};
const Org = React.PropTypes.shape({
...BaseObject,
owner: React.PropTypes.string
});
const Project = React.PropTypes.shape({
...BaseObject
});
const Service = React.PropTypes.shape({
...BaseObject
});
const Sections = React.PropTypes.arrayOf(
React.PropTypes.string
);
module.exports = {
org: Org,
project: Project,
sections: Sections,
service: Service
};

View File

@ -10,6 +10,7 @@ module.exports = () => {
app: require('@state/reducers/app'), app: require('@state/reducers/app'),
intl: require('@state/reducers/intl'), intl: require('@state/reducers/intl'),
orgs: require('@state/reducers/orgs'), orgs: require('@state/reducers/orgs'),
projects: require('@state/reducers/projects') projects: require('@state/reducers/projects'),
services: require('@state/reducers/services')
}); });
}; };

View File

@ -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
}, {});

View File

@ -11,17 +11,24 @@ const account = (state) => get(state, 'account.data', {});
const accountUi = (state) => get(state, 'account.ui', {}); const accountUi = (state) => get(state, 'account.ui', {});
const orgUiSections = (state) => get(state, 'orgs.ui.sections', []); const orgUiSections = (state) => get(state, 'orgs.ui.sections', []);
const projectUiSections = (state) => get(state, 'projects.ui.sections', []); const projectUiSections = (state) => get(state, 'projects.ui.sections', []);
const serviceUiSections = (state) => get(state, 'services.ui.sections', []);
const orgs = (state) => get(state, 'orgs.data', []); const orgs = (state) => get(state, 'orgs.data', []);
const projects = (state) => get(state, 'projects.data', []); const projects = (state) => get(state, 'projects.data', []);
const services = (state) => get(state, 'services.data', []);
const projectById= (id) => createSelector( const projectById = (projectId) => createSelector(
projects, projects,
(projects) => find(projects, ['id', id]) (projects) => find(projects, ['id', projectId])
); );
const orgById = (id) => createSelector( const orgById = (orgId) => createSelector(
orgs, orgs,
(orgs) => find(orgs, ['id', id]) (orgs) => find(orgs, ['id', orgId])
);
const serviceById = (serviceId) => createSelector(
[services],
(services) => find(services, ['id', serviceId])
); );
const projectsByOrgId = (orgId) => createSelector( const projectsByOrgId = (orgId) => createSelector(
@ -36,13 +43,28 @@ const orgSections = (orgId) => createSelector(
) )
); );
const servicesByProjectId = (projectId) => createSelector(
[services, projectById(projectId)],
(services, project) =>
services.filter((s) => s.project === project.uuid)
.map((service) => ({
...service,
services: services.filter((s) => s.parent === service.uuid)
}))
.filter((s) => !s.parent)
);
module.exports = { module.exports = {
accountSelector: account, accountSelector: account,
accountUISelector: accountUi, accountUISelector: accountUi,
orgByIdSelector: orgById, orgByIdSelector: orgById,
orgsSelector: orgs, orgsSelector: orgs,
servicesSelector: services,
serviceByIdSelector: serviceById,
orgSectionsSelector: orgSections, orgSectionsSelector: orgSections,
projectSectionsSelector: projectUiSections, projectSectionsSelector: projectUiSections,
serviceSectionsSelector: serviceUiSections,
projectsByOrgIdSelector: projectsByOrgId, projectsByOrgIdSelector: projectsByOrgId,
projectByIdSelector: projectById projectByIdSelector: projectById,
servicesByProjectIdSelector: servicesByProjectId
}; };