Rock and roll
This commit is contained in:
parent
131923ca59
commit
7e2bfce707
50
frontend-technical-prototype/README.md
Normal file
50
frontend-technical-prototype/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
[![Docker Repository on Quay](https://quay.io/repository/yldio/joyent-dashboard-frontend/status?token=ab124f42-c6c5-4023-9841-17ef74c8b7f1 "Docker Repository on Quay")](https://quay.io/repository/yldio/joyent-dashboard-frontend)
|
||||
# Joyent Dashboard Frontend
|
||||
|
||||
## start
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
## test
|
||||
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
## structure
|
||||
|
||||
```
|
||||
.
|
||||
├── src
|
||||
│ ├── containers
|
||||
│ ├── index.js
|
||||
│ ├── root.js
|
||||
│ └── state
|
||||
│ ├── actions.js
|
||||
│ ├── reducers
|
||||
│ ├── store.js
|
||||
│ └── thunks
|
||||
├── static
|
||||
├── locales
|
||||
├── scripts
|
||||
├── test
|
||||
├── webpack
|
||||
├── .babelrc
|
||||
└── .eslintrc
|
||||
```
|
||||
|
||||
- **src/index.js**: Renders `src/root.js` and bootstraps hot module reloading.
|
||||
- **src/root.js**: The main component that wraps `react-redux`, `react-router` and `react-hot-loader`.
|
||||
- **src/state/store.js**: Exports a function that creates a `redux` store instance with all the middlewares and reducers configured.
|
||||
- **src/state/actions.js**: Not only exports all the actions available (declared in the file), but also goes through all the thunks and exports them.
|
||||
- **src/state/thunks**: Directory to place thunks so that actions or reducers don't get too confusing.
|
||||
- **src/state/reducers**: Each file here represents a reducer scope. So, `state.app` will be controlled in `reducers/app.js`.
|
||||
- **locales**: Translation definitions for each locale supported.
|
||||
- **scripts**: Utility scripts (e.g. building localizations).
|
||||
- **test**: Self explanatory.
|
||||
- **webpack**: Webpack configuration for multiple enviroments. Development configuration includes a dev-server and hot module replacement support.
|
||||
- **.babelrc**: This babel configuration outputs ES2015 code, so it will produce code only for modern browsers.
|
||||
Also, async/await is supported.
|
||||
- **.eslintrc**:ESLint configuration. It's basically [semistandard](https://github.com/Flet/semistandard) with `space-before-function-paren` probited;
|
134
frontend-technical-prototype/package.json
Normal file
134
frontend-technical-prototype/package.json
Normal file
@ -0,0 +1,134 @@
|
||||
{
|
||||
"name": "joyent-portal-frontend",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"license": "private",
|
||||
"main": "server/index.js",
|
||||
"directories": {
|
||||
"test": "test",
|
||||
"lib": "src"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --define process.env.BASELINE_GRID=false --open --config webpack/index.js ",
|
||||
"production": "node server",
|
||||
"lint": "make lint",
|
||||
"lint-fix": "make lint-fix",
|
||||
"test": "make test",
|
||||
"open": "nyc report --reporter=html & open coverage/index.html",
|
||||
"coverage": "nyc check-coverage --statements 100 --functions 100 --lines 100 --branches 100",
|
||||
"build-locales": " CONFIG=$(pwd)/webpack/index.js babel-node scripts/build-locales",
|
||||
"clean-static": "git check-ignore static/** | xargs rm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tomgco/joyent-portal-ui": "alpha",
|
||||
"constant-case": "^2.0.0",
|
||||
"force-array": "^3.1.0",
|
||||
"hapi": "^16.1.0",
|
||||
"inert": "^4.1.0",
|
||||
"inherits": "^2.0.3",
|
||||
"lodash.find": "^4.6.0",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isempty": "^4.4.0",
|
||||
"lodash.template": "^4.4.0",
|
||||
"moment": "^2.17.1",
|
||||
"param-case": "^2.1.0",
|
||||
"portal-api": "^1.0.0",
|
||||
"querystring": "^0.2.0",
|
||||
"react": "^15.4.2",
|
||||
"react-a11y": "^0.3.3",
|
||||
"react-dom": "^15.4.2",
|
||||
"react-intl": "^2.2.3",
|
||||
"react-intl-redux": "^0.4.1",
|
||||
"react-redux": "^5.0.3",
|
||||
"react-router": "4.0.0-beta.6",
|
||||
"react-router-dom": "4.0.0-beta.6",
|
||||
"react-select": "^1.0.0-rc.3",
|
||||
"reduce-reducers": "^0.1.2",
|
||||
"redux": "^3.6.0",
|
||||
"redux-actions": "^1.2.2",
|
||||
"redux-batched-actions": "^0.1.5",
|
||||
"redux-form": "^6.5.0",
|
||||
"redux-logger": "^2.8.1",
|
||||
"redux-promise-middleware": "^4.2.0",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"reselect": "^2.5.4",
|
||||
"simple-statistics": "^2.5.0",
|
||||
"styled-components": "^1.4.4",
|
||||
"understood": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.18.2",
|
||||
"babel-cli": "^6.23.0",
|
||||
"babel-core": "^6.23.1",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.3.2",
|
||||
"babel-plugin-inline-react-svg": "^0.2.0",
|
||||
"babel-plugin-styled-components": "^1.0.0",
|
||||
"babel-plugin-transform-class-properties": "^6.23.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
|
||||
"babel-plugin-transform-es2015-parameters": "^6.23.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-plugin-transform-react-constant-elements": "^6.23.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.23.0",
|
||||
"babel-plugin-transform-react-jsx-self": "^6.22.0",
|
||||
"babel-plugin-transform-react-jsx-source": "^6.22.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-plugin-webpack-alias": "^2.1.2",
|
||||
"babel-plugin-webpack-loaders": "^0.9.0",
|
||||
"babel-preset-env": "^1.1.10",
|
||||
"babel-preset-react": "^6.23.0",
|
||||
"babel-register": "^6.23.0",
|
||||
"case-sensitive-paths-webpack-plugin": "^1.1.4",
|
||||
"css-loader": "^0.26.2",
|
||||
"enzyme": "^2.7.1",
|
||||
"eslint": "^3.16.1",
|
||||
"eslint-config-semistandard": "^7.0.0",
|
||||
"eslint-config-standard": "^7.0.0",
|
||||
"eslint-plugin-babel": "^4.1.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-react": "^6.10.0",
|
||||
"eslint-plugin-standard": "^2.0.1",
|
||||
"file-loader": "^0.10.1",
|
||||
"jsdom": "^9.11.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"ncp": "^2.0.0",
|
||||
"node-hook": "^0.4.0",
|
||||
"nyc": "^10.1.2",
|
||||
"pre-commit": "^1.2.2",
|
||||
"react-addons-perf": "^15.4.2",
|
||||
"react-addons-test-utils": "^15.4.2",
|
||||
"react-dev-utils": "^0.5.1",
|
||||
"react-perf": "^1.0.1",
|
||||
"redux-ava": "^2.2.0",
|
||||
"redux-perf-middleware": "^1.2.2",
|
||||
"require-hacker": "^2.1.4",
|
||||
"simple-mock": "^0.7.3",
|
||||
"style-loader": "^0.13.2",
|
||||
"stylelint": "^7.9.0",
|
||||
"stylelint-config-standard": "^16.0.0",
|
||||
"stylelint-processor-styled-components": "^0.0.4",
|
||||
"tap-xunit": "^1.7.0",
|
||||
"thenify": "^3.2.1",
|
||||
"url-loader": "^0.5.8",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.4.1",
|
||||
"webpack-manifest-plugin": "^1.1.0",
|
||||
"webpack-shell-plugin": "^0.5.0"
|
||||
},
|
||||
"ava": {
|
||||
"failFast": true,
|
||||
"cache": false,
|
||||
"require": [
|
||||
"./test/_hook.js"
|
||||
],
|
||||
"babel": "inherit"
|
||||
},
|
||||
"pre-commit": [
|
||||
"lint",
|
||||
"test",
|
||||
"coverage"
|
||||
]
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import styled from 'styled-components';
|
||||
import Container from '@ui/components/container';
|
||||
import { breakpoints } from '@ui/shared/constants';
|
||||
|
||||
const LayoutContainer = styled(Container)`
|
||||
padding: 2rem;
|
||||
|
||||
${breakpoints.large`
|
||||
padding: 0;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default LayoutContainer;
|
@ -0,0 +1 @@
|
||||
export { default as LayoutContainer } from './container';
|
@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import flatten from 'lodash.flatten';
|
||||
|
||||
import Container from '@ui/components/container';
|
||||
import Row from '@ui/components/row';
|
||||
import Column from '@ui/components/column';
|
||||
import { H2 } from '@ui/components/base-elements';
|
||||
import NavLink from '@ui/components/nav-link';
|
||||
import PropTypes from '@root/prop-types';
|
||||
import { remcalc, unitcalc } from '@ui/shared/functions';
|
||||
import { colors } from '@ui/shared/constants';
|
||||
|
||||
// Main Contonent Wrapper Styles
|
||||
const StyledDiv = styled.div`
|
||||
border-bottom: solid ${remcalc(1)} ${colors.base.grey};
|
||||
padding: ${unitcalc(4.5)} 0 ${unitcalc(4.5)} 0;
|
||||
margin-bottom: ${remcalc(18)};
|
||||
`;
|
||||
|
||||
const BreadcrumbA = styled(NavLink)`
|
||||
text-decoration: none;
|
||||
color: ${colors.base.primary};
|
||||
`;
|
||||
|
||||
const BreadcrumbSpan = styled.span`
|
||||
color: ${colors.base.text};
|
||||
`;
|
||||
|
||||
function getNameLink(name) {
|
||||
return flatten(name.map((part, i) => {
|
||||
if (!part.name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const link = (
|
||||
<BreadcrumbA key={part.pathname} to={part.pathname}>
|
||||
{part.name}
|
||||
</BreadcrumbA>
|
||||
);
|
||||
|
||||
const key = `${part.pathname}${i}`;
|
||||
const slash = (
|
||||
<BreadcrumbSpan key={key}> / </BreadcrumbSpan>
|
||||
);
|
||||
|
||||
return (i === 0) ? link : [
|
||||
slash,
|
||||
link
|
||||
];
|
||||
}));
|
||||
}
|
||||
|
||||
const StyledH2 = styled(H2)`
|
||||
color: ${colors.base.primary};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const Breadcrumb = ({
|
||||
name = []
|
||||
}) => (
|
||||
<Container>
|
||||
<Row>
|
||||
<Column xs={12}>
|
||||
<StyledDiv>
|
||||
<StyledH2>
|
||||
{getNameLink(name)}
|
||||
</StyledH2>
|
||||
</StyledDiv>
|
||||
</Column>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
|
||||
Breadcrumb.propTypes = {
|
||||
name: React.PropTypes.arrayOf(PropTypes.link)
|
||||
};
|
||||
|
||||
export default Breadcrumb;
|
@ -0,0 +1,2 @@
|
||||
export { default as Breadcrumb } from './breadcrumb';
|
||||
export { default as Menu } from './menu';
|
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Li from '@ui/components/horizontal-list/li';
|
||||
import NavLink from '@ui/components/nav-link';
|
||||
import { breakpoints } from '@ui/shared/constants';
|
||||
import { remcalc } from '@ui/shared/functions';
|
||||
import PropTypes from '@root/prop-types';
|
||||
import Ul from '@ui/components/horizontal-list/ul';
|
||||
import { LayoutContainer } from '@components/layout';
|
||||
|
||||
const StyledHorizontalList = styled(Ul)`
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const StyledHorizontalListItem = styled(Li)`
|
||||
${breakpoints.smallOnly`
|
||||
display: block;
|
||||
`}
|
||||
|
||||
& + li {
|
||||
margin-left: ${remcalc(21)};
|
||||
}
|
||||
`;
|
||||
|
||||
const Menu = ({
|
||||
links
|
||||
}) => {
|
||||
|
||||
const navLinks = links.map((link) => {
|
||||
return (
|
||||
<StyledHorizontalListItem key={link.name}>
|
||||
<NavLink activeClassName='active' to={link.pathname}>
|
||||
<FormattedMessage id={link.name} />
|
||||
</NavLink>
|
||||
</StyledHorizontalListItem>
|
||||
);
|
||||
});
|
||||
|
||||
// TODO this could be any kind of nav, not just 'project-'...
|
||||
return (
|
||||
<LayoutContainer>
|
||||
<StyledHorizontalList name='project-nav'>
|
||||
{navLinks}
|
||||
</StyledHorizontalList>
|
||||
</LayoutContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Menu.propTypes = {
|
||||
links: React.PropTypes.arrayOf(PropTypes.link)
|
||||
};
|
||||
|
||||
export default Menu;
|
@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
orgByIdSelector,
|
||||
projectByIdSelector,
|
||||
serviceByIdSelector
|
||||
} from '@root/state/selectors';
|
||||
import { Breadcrumb as BreadcrumbComponent } from '@components/navigation';
|
||||
|
||||
const Breadcrumb = ({
|
||||
location,
|
||||
match,
|
||||
org,
|
||||
project,
|
||||
service
|
||||
}) => {
|
||||
|
||||
const path = location.pathname.split('/');
|
||||
|
||||
const links = [{
|
||||
name: org.name,
|
||||
pathname: path.slice(0, 2).join('/')
|
||||
}];
|
||||
|
||||
if(project) {
|
||||
links.push({
|
||||
name: project.name,
|
||||
pathname: path.slice(0, 4).join('/')
|
||||
});
|
||||
}
|
||||
|
||||
if(service) {
|
||||
links.push({
|
||||
name: service.name,
|
||||
pathname: path.slice(0, 6).join('/')
|
||||
});
|
||||
}
|
||||
|
||||
// TODO add people etc
|
||||
|
||||
return (
|
||||
<BreadcrumbComponent name={links} />
|
||||
);
|
||||
};
|
||||
|
||||
Breadcrumb.propTypes = {
|
||||
location: React.PropTypes.object.isRequired,
|
||||
match: React.PropTypes.object.isRequired,
|
||||
org: React.PropTypes.object.isRequired,
|
||||
project: React.PropTypes.object,
|
||||
service: React.PropTypes.object
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, {
|
||||
location,
|
||||
match = {
|
||||
params: {}
|
||||
}
|
||||
}) => ({
|
||||
location,
|
||||
match,
|
||||
org: orgByIdSelector(match.params.org)(state),
|
||||
project: projectByIdSelector(match.params.project)(state),
|
||||
service: serviceByIdSelector(match.params.service)(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Breadcrumb);
|
@ -0,0 +1,3 @@
|
||||
export { default as Breadcrumb } from './breadcrumb';
|
||||
export { default as Menu } from './menu';
|
||||
export { default as Org } from './org';
|
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
orgSectionsByIdSelector,
|
||||
projectSectionsSelector,
|
||||
serviceSectionsSelector
|
||||
} from '@root/state/selectors';
|
||||
import { Menu as MenuComponent } from '@components/navigation';
|
||||
|
||||
const Menu = (props) => {
|
||||
|
||||
const {
|
||||
match,
|
||||
sections
|
||||
} = props;
|
||||
|
||||
const links = sections.map((section) => ({
|
||||
name: section,
|
||||
pathname: `${match.url}/${section}`
|
||||
}));
|
||||
|
||||
return (
|
||||
<MenuComponent links={links} />
|
||||
);
|
||||
};
|
||||
|
||||
Menu.propTypes = {
|
||||
match: React.PropTypes.object.isRequired,
|
||||
sections: React.PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, {
|
||||
match = {
|
||||
params: {}
|
||||
}
|
||||
}) => ({
|
||||
location,
|
||||
match,
|
||||
sections: match.params.service ?
|
||||
serviceSectionsSelector(state) :
|
||||
match.params.project ?
|
||||
projectSectionsSelector(state) :
|
||||
orgSectionsByIdSelector(match.params.org)(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Menu);
|
@ -0,0 +1,8 @@
|
||||
export { default as ServiceActivityFeed } from './activity-feed';
|
||||
export { default as ServiceFirewall } from './firewall';
|
||||
export { default as ServiceInstances } from './instances';
|
||||
export { default as ServiceMetrics } from './metrics';
|
||||
export { default as ServiceNetworks } from './networks';
|
||||
export { default as ServiceManifest } from './service-manifest';
|
||||
export { default as ServiceSummary } from './summary';
|
||||
export { default as ServiceMetadata } from './tags-metadata';
|
@ -0,0 +1,46 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { addMetric, metricDurationChange } from '@state/actions';
|
||||
import Metrics from '@containers/metrics';
|
||||
|
||||
import {
|
||||
metricsByServiceIdSelector,
|
||||
metricTypesSelector,
|
||||
serviceByIdSelector
|
||||
} from '@state/selectors';
|
||||
|
||||
const mapStateToProps = (state, {
|
||||
match = {
|
||||
params: {}
|
||||
}
|
||||
}) => ({
|
||||
datasets: metricsByServiceIdSelector(match.params.service)(state),
|
||||
metricTypes: metricTypesSelector(state),
|
||||
service: serviceByIdSelector(match.params.service)(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
addMetric: (service) => (metric) =>
|
||||
dispatch(addMetric({
|
||||
metric: metric,
|
||||
service: service.uuid
|
||||
})),
|
||||
metricDurationChange: (service) => (duration, dataset) =>
|
||||
dispatch(metricDurationChange({
|
||||
duration,
|
||||
dataset
|
||||
}))
|
||||
});
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
|
||||
...stateProps,
|
||||
...dispatchProps,
|
||||
...ownProps,
|
||||
addMetric: dispatchProps.addMetric(stateProps.service),
|
||||
metricDurationChange: dispatchProps.metricDurationChange(stateProps.service)
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(Metrics);
|
@ -0,0 +1,3 @@
|
||||
export { default as ServicesTopology } from './topology';
|
||||
export { default as ServicesList } from './list';
|
||||
export { default as ServicesView } from './view';
|
138
frontend-technical-prototype/src/containers/services/list.js
Normal file
138
frontend-technical-prototype/src/containers/services/list.js
Normal file
@ -0,0 +1,138 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from '@root/prop-types';
|
||||
import { LayoutContainer } from '@components/layout';
|
||||
import ServiceItem from '@components/service/item';
|
||||
import UnmanagedInstances from '@components/services/unmanaged-instances';
|
||||
import { toggleTooltip } from '@state/actions';
|
||||
import ServicesTooltip from '@components/services/tooltip';
|
||||
import { subscribeMetric } from '@state/thunks';
|
||||
|
||||
import {
|
||||
orgByIdSelector,
|
||||
projectByIdSelector,
|
||||
servicesByProjectIdSelector,
|
||||
serviceUiTooltipSelector
|
||||
} from '@state/selectors';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
// TMP - single source of truth
|
||||
const duration = '5 minutes';
|
||||
const interval = '15 seconds';
|
||||
|
||||
class Services extends React.Component {
|
||||
// we DON'T want to unsubscribe once we started going
|
||||
componentWillMount() {
|
||||
this.props.subscribeMetric(interval);
|
||||
}
|
||||
|
||||
ref(name) {
|
||||
this._refs = this._refs || {};
|
||||
|
||||
return (el) => {
|
||||
this._refs[name] = el;
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
org = {},
|
||||
project = {},
|
||||
services = [],
|
||||
toggleTooltip = () => ({}),
|
||||
uiTooltip = {}
|
||||
} = this.props;
|
||||
|
||||
const onQuickActions = (evt, service) => {
|
||||
const list = this._refs.container;
|
||||
const listRect = list.getBoundingClientRect();
|
||||
const button = evt.currentTarget;
|
||||
const buttonRect = button.getBoundingClientRect();
|
||||
|
||||
const position = {
|
||||
left: buttonRect.left - listRect.left
|
||||
+ (buttonRect.right - buttonRect.left)/2,
|
||||
top: buttonRect.bottom - listRect.top
|
||||
};
|
||||
|
||||
toggleTooltip({
|
||||
service: service,
|
||||
position: position,
|
||||
data: {
|
||||
serviceId: service.id,
|
||||
orgId: org.id,
|
||||
projectId: project.id
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleTooltipBlur = (evt) => onQuickActions(evt, uiTooltip.service);
|
||||
|
||||
const serviceList = services.map((service) => (
|
||||
<ServiceItem
|
||||
key={service.uuid}
|
||||
onQuickActions={onQuickActions}
|
||||
org={org.id}
|
||||
project={project.id}
|
||||
service={service}
|
||||
uiTooltip={uiTooltip}
|
||||
/>
|
||||
));
|
||||
|
||||
// TODO replace `false` with a check for existence unmanaged instances
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
const unmanagedInstances = false ? (
|
||||
<UnmanagedInstances instances={0} />
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<LayoutContainer>
|
||||
{unmanagedInstances}
|
||||
<StyledContainer>
|
||||
<div ref={this.ref('container')}>
|
||||
{serviceList}
|
||||
<ServicesTooltip {...uiTooltip} onBlur={handleTooltipBlur} />
|
||||
</div>
|
||||
</StyledContainer>
|
||||
</LayoutContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Services.propTypes = {
|
||||
org: PropTypes.org,
|
||||
project: PropTypes.project,
|
||||
services: React.PropTypes.arrayOf(PropTypes.service),
|
||||
toggleTooltip: React.PropTypes.func,
|
||||
uiTooltip: React.PropTypes.object,
|
||||
subscribeMetric: React.PropTypes.func
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, {
|
||||
match = {
|
||||
params: {}
|
||||
},
|
||||
push
|
||||
}) => ({
|
||||
org: orgByIdSelector(match.params.org)(state),
|
||||
project: projectByIdSelector(match.params.project)(state),
|
||||
services: servicesByProjectIdSelector(match.params.project, {
|
||||
duration,
|
||||
interval
|
||||
})(state),
|
||||
uiTooltip: serviceUiTooltipSelector(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleTooltip: (data) => dispatch(toggleTooltip(data)),
|
||||
subscribeMetric: (payload) => dispatch(subscribeMetric(payload))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Services);
|
120
frontend-technical-prototype/src/index.js
Normal file
120
frontend-technical-prototype/src/index.js
Normal file
@ -0,0 +1,120 @@
|
||||
import { IntlProvider } from 'react-intl-redux';
|
||||
import { Provider } from 'react-redux';
|
||||
import qs from 'querystring';
|
||||
import a11y from 'react-a11y';
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import Perf from 'react-addons-perf';
|
||||
|
||||
import MockStateTesting from '@mock-states/testing';
|
||||
import MockState from '@mock-states';
|
||||
import LeakDatasets from './dataset-leak.json';
|
||||
import NormalDatasets from './dataset-normal.json';
|
||||
import Store from '@state/store';
|
||||
import { isProduction } from '@utils';
|
||||
import Router from '@root/routing';
|
||||
|
||||
if ( !isProduction() ) {
|
||||
a11y(React, {
|
||||
ReactDOM
|
||||
});
|
||||
|
||||
window.Perf = Perf;
|
||||
}
|
||||
|
||||
const states = {
|
||||
all: MockState,
|
||||
testing: MockStateTesting
|
||||
};
|
||||
|
||||
const query = qs.parse(window.location.search.replace(/^\?/, ''));
|
||||
const mockState = states[query.mock || 'all'];
|
||||
|
||||
// node_memory_rss_bytes
|
||||
// node_memory_heap_total_bytes
|
||||
// node_memory_heap_used_bytes
|
||||
// process_heap_bytes
|
||||
// process_resident_memory_bytes
|
||||
// process_virtual_memory_bytes
|
||||
// process_cpu_seconds_total
|
||||
// process_cpu_system_seconds_total
|
||||
// process_cpu_user_seconds_total
|
||||
// node_lag_duration_milliseconds
|
||||
// http_request_duration_milliseconds
|
||||
|
||||
// node_memory_rss_bytes
|
||||
// node_memory_heap_total_bytes
|
||||
// node_memory_heap_used_bytes
|
||||
// process_heap_bytes
|
||||
// process_resident_memory_bytes
|
||||
// process_virtual_memory_bytes
|
||||
// process_cpu_seconds_total
|
||||
// process_cpu_system_seconds_total
|
||||
// process_cpu_user_seconds_total
|
||||
// node_lag_duration_milliseconds
|
||||
// http_request_duration_milliseconds
|
||||
|
||||
// TMP - ensure datasets are at least 2 hrs long - START
|
||||
import getTwoHourDatasets from './utils/two-hour-metric-datasets';
|
||||
const leakTwoHourLongDatasets = getTwoHourDatasets(LeakDatasets);
|
||||
const normalTwoHourLongDatasets = getTwoHourDatasets(NormalDatasets);
|
||||
// TMP - ensure datasets are at least 2 hrs long - END
|
||||
|
||||
// TMP - plug fake metric data - START
|
||||
const isCrazy = (uuid) => uuid === 'crazy-cpu' ||
|
||||
uuid === 'crazy-disk' || uuid === 'crazy-memory';
|
||||
|
||||
const isCPU = (uuid) => uuid === 'crazy-cpu'
|
||||
|| uuid === '3e6ee79a-7453-4fc6-b9da-7ae1e41138ec';
|
||||
|
||||
const isDisk = (uuid) => uuid === 'crazy-disk'
|
||||
|| uuid === '4e6ee79a-7453-4fc6-b9da-7ae1e41138ed';
|
||||
|
||||
const isMemory = (uuid) => uuid === 'crazy-memory'
|
||||
|| uuid === '6e6ee79a-7453-4fc6-b9da-7ae1e41138ed';
|
||||
|
||||
const getDataset = (twoHourLongDatasets, uuid) => {
|
||||
if(isCPU(uuid)) {
|
||||
return twoHourLongDatasets.process_cpu_seconds_total;
|
||||
}
|
||||
if(isDisk(uuid)) {
|
||||
return twoHourLongDatasets.process_heap_bytes.map((sample) =>
|
||||
[
|
||||
sample[0],
|
||||
sample[1]/1024/1024
|
||||
]
|
||||
);
|
||||
}
|
||||
if(isMemory(uuid)) {
|
||||
return twoHourLongDatasets.node_memory_heap_used_bytes.map((sample) =>
|
||||
[
|
||||
sample[0],
|
||||
sample[1]/1024/1024
|
||||
]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const datasets = MockState.metrics.data.datasets.map((dataset, index) => {
|
||||
|
||||
const data = isCrazy(dataset.uuid) && dataset.uuid !== 'crazy-cpu' ?
|
||||
getDataset(leakTwoHourLongDatasets, dataset.uuid) :
|
||||
getDataset(normalTwoHourLongDatasets, dataset.uuid);
|
||||
|
||||
return {
|
||||
...dataset,
|
||||
data: data
|
||||
};
|
||||
});
|
||||
|
||||
mockState.metrics.data.datasets = datasets;
|
||||
// TMP - plug fake metric data - END
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={Store(mockState)}>
|
||||
<IntlProvider>
|
||||
{Router}
|
||||
</IntlProvider>
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user