Compare commits
No commits in common. "master" and "nav-copy" have entirely different histories.
@ -1,9 +1,4 @@
|
|||||||
|
packages/*/**
|
||||||
|
prototypes/*/**
|
||||||
artifacts
|
artifacts
|
||||||
reports
|
reports
|
||||||
.nyc_output
|
|
||||||
coverage
|
|
||||||
dist
|
|
||||||
styleguide
|
|
||||||
build
|
|
||||||
consoles/*/lib/app
|
|
||||||
node_modules
|
|
@ -1,10 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "joyent-portal",
|
"extends": "joyent-portal",
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": 1,
|
|
||||||
"new-cap": 0,
|
|
||||||
"jsx-a11y/href-no-hash": 0,
|
"jsx-a11y/href-no-hash": 0,
|
||||||
"no-negated-condition": 1,
|
"new-cap": 0,
|
||||||
"camelcase": 1
|
"no-console": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
.gitignore
vendored
@ -165,5 +165,3 @@ prototypes/*/package-lock.json
|
|||||||
|
|
||||||
_env*
|
_env*
|
||||||
keys*
|
keys*
|
||||||
/packages/*/public/index.html
|
|
||||||
/consoles/*/public/index.html
|
|
||||||
|
@ -15,7 +15,6 @@ yarn.lock
|
|||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
packages/*/lib/app
|
packages/*/lib/app
|
||||||
consoles/*/lib/app
|
|
||||||
|
|
||||||
*.ico
|
*.ico
|
||||||
*.html
|
*.html
|
||||||
|
1
.vscode/settings.json
vendored
@ -1 +0,0 @@
|
|||||||
{}
|
|
@ -9,23 +9,24 @@
|
|||||||
"build:lib": "echo 0",
|
"build:lib": "echo 0",
|
||||||
"build:bundle": "echo 0",
|
"build:bundle": "echo 0",
|
||||||
"prepublish": "echo 0",
|
"prepublish": "echo 0",
|
||||||
|
"lint": "echo 0",
|
||||||
|
"lint:ci": "echo 0",
|
||||||
"test": "echo 0",
|
"test": "echo 0",
|
||||||
"test:ci": "echo 0"
|
"test:ci": "echo 0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"apr-main": "^4.0.3",
|
"apr-main": "^4.0.3",
|
||||||
"cloudapi-gql": "^8.0.0",
|
"brule": "^3.1.0",
|
||||||
|
"cloudapi-gql": "^7.1.4",
|
||||||
"execa": "^0.10.0",
|
"execa": "^0.10.0",
|
||||||
"graphi": "^5.7.0",
|
"h2o2": "^8.0.1",
|
||||||
"h2o2": "^8.1.2",
|
"hapi": "^17.3.1",
|
||||||
"hapi": "^17.5.0",
|
"hapi-triton-auth": "^2.0.1",
|
||||||
"hapi-triton-auth": "^3.0.0",
|
"hapi-webconsole-nav": "^1.2.0",
|
||||||
"hapi-webconsole-nav": "^2.1.0",
|
"inert": "^5.1.0",
|
||||||
"my-joy-images": "*",
|
"my-joy-images": "*",
|
||||||
"my-joy-instances": "*",
|
"my-joy-instances": "*",
|
||||||
"my-joy-navigation": "*",
|
"my-joy-navigation": "*",
|
||||||
"my-joy-service-groups": "*",
|
"rollover": "^1.0.0"
|
||||||
"my-joy-templates": "*",
|
|
||||||
"tsg-graphql": "^1.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
require('../.env.js');
|
|
||||||
|
|
||||||
const Main = require('apr-main');
|
const Main = require('apr-main');
|
||||||
const CloudApiGql = require('cloudapi-gql');
|
const CloudApiGql = require('cloudapi-gql');
|
||||||
const Graphi = require('graphi');
|
|
||||||
const Url = require('url');
|
const Url = require('url');
|
||||||
|
|
||||||
const Server = require('./server');
|
const Server = require('./server');
|
||||||
const Ui = require('my-joy-images');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
PORT = 4003,
|
PORT = 4003,
|
||||||
@ -30,35 +25,19 @@ Main(async () => {
|
|||||||
BASE_URL
|
BASE_URL
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register([
|
await server.register({
|
||||||
{
|
plugin: CloudApiGql,
|
||||||
plugin: Graphi,
|
options: {
|
||||||
options: {
|
authStrategy: 'sso',
|
||||||
graphqlPath: '/graphql',
|
keyPath,
|
||||||
graphiqlPath: '/graphiql',
|
keyId,
|
||||||
authStrategy: 'sso'
|
apiBaseUrl,
|
||||||
},
|
dcName
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
routes: {
|
||||||
plugin: CloudApiGql,
|
prefix: `/${PREFIX}`
|
||||||
options: {
|
|
||||||
authStrategy: 'sso',
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl,
|
|
||||||
dcName
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Ui
|
|
||||||
}
|
}
|
||||||
]);
|
});
|
||||||
|
|
||||||
await server.start();
|
await server.start();
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
require('../.env.js');
|
|
||||||
|
|
||||||
const Main = require('apr-main');
|
const Main = require('apr-main');
|
||||||
const CloudApiGql = require('cloudapi-gql');
|
const CloudApiGql = require('cloudapi-gql');
|
||||||
const Graphi = require('graphi');
|
|
||||||
const Url = require('url');
|
const Url = require('url');
|
||||||
|
|
||||||
const Server = require('./server');
|
const Server = require('./server');
|
||||||
const Ui = require('my-joy-instances');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
PORT = 4002,
|
PORT = 4002,
|
||||||
@ -30,35 +25,19 @@ Main(async () => {
|
|||||||
BASE_URL
|
BASE_URL
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register([
|
await server.register({
|
||||||
{
|
plugin: CloudApiGql,
|
||||||
plugin: Graphi,
|
options: {
|
||||||
options: {
|
authStrategy: 'sso',
|
||||||
graphqlPath: '/graphql',
|
keyPath,
|
||||||
graphiqlPath: '/graphiql',
|
keyId,
|
||||||
authStrategy: 'sso'
|
apiBaseUrl,
|
||||||
},
|
dcName
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
routes: {
|
||||||
plugin: CloudApiGql,
|
prefix: `/${PREFIX}`
|
||||||
options: {
|
|
||||||
authStrategy: 'sso',
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl,
|
|
||||||
dcName
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Ui
|
|
||||||
}
|
}
|
||||||
]);
|
});
|
||||||
|
|
||||||
await server.start();
|
await server.start();
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
require('../.env.js');
|
|
||||||
|
|
||||||
const Main = require('apr-main');
|
const Main = require('apr-main');
|
||||||
const Nav = require('hapi-webconsole-nav');
|
const Nav = require('hapi-webconsole-nav');
|
||||||
const Graphi = require('graphi');
|
|
||||||
const Url = require('url');
|
const Url = require('url');
|
||||||
|
|
||||||
const Server = require('./server');
|
const Server = require('./server');
|
||||||
const Ui = require('my-joy-navigation');
|
|
||||||
|
|
||||||
const Regions = require('../data/regions');
|
const Regions = require('../data/regions');
|
||||||
const Categories = require('../data/categories');
|
const Categories = require('../data/categories');
|
||||||
@ -35,38 +30,22 @@ Main(async () => {
|
|||||||
BASE_URL
|
BASE_URL
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register([
|
await server.register({
|
||||||
{
|
plugin: Nav,
|
||||||
plugin: Graphi,
|
options: {
|
||||||
options: {
|
keyPath,
|
||||||
graphqlPath: '/graphql',
|
keyId,
|
||||||
graphiqlPath: '/graphiql',
|
apiBaseUrl,
|
||||||
authStrategy: 'sso'
|
dcName,
|
||||||
},
|
baseUrl,
|
||||||
routes: {
|
regions: Regions,
|
||||||
prefix: `/${PREFIX}`
|
accountServices: Account,
|
||||||
}
|
categories: Categories
|
||||||
},
|
},
|
||||||
{
|
routes: {
|
||||||
plugin: Nav,
|
prefix: `/${PREFIX}`
|
||||||
options: {
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl,
|
|
||||||
dcName,
|
|
||||||
baseUrl,
|
|
||||||
regions: Regions,
|
|
||||||
accountServices: Account,
|
|
||||||
categories: Categories
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Ui
|
|
||||||
}
|
}
|
||||||
]);
|
});
|
||||||
|
|
||||||
await server.start();
|
await server.start();
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
require('../.env.js');
|
|
||||||
|
|
||||||
const Hapi = require('hapi');
|
const Hapi = require('hapi');
|
||||||
const Sso = require('hapi-triton-auth');
|
const Sso = require('hapi-triton-auth');
|
||||||
|
const Url = require('url');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
COOKIE_PASSWORD,
|
COOKIE_PASSWORD,
|
||||||
@ -9,10 +8,12 @@ const {
|
|||||||
SDC_KEY_PATH,
|
SDC_KEY_PATH,
|
||||||
SDC_ACCOUNT,
|
SDC_ACCOUNT,
|
||||||
SDC_KEY_ID,
|
SDC_KEY_ID,
|
||||||
SDC_URL
|
SDC_URL,
|
||||||
|
DC_NAME
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
module.exports = async ({ PORT, BASE_URL }) => {
|
module.exports = async ({ PORT, BASE_URL }) => {
|
||||||
|
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
||||||
const keyPath = SDC_KEY_PATH;
|
const keyPath = SDC_KEY_PATH;
|
||||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||||
const apiBaseUrl = SDC_URL;
|
const apiBaseUrl = SDC_URL;
|
||||||
@ -49,7 +50,6 @@ module.exports = async ({ PORT, BASE_URL }) => {
|
|||||||
|
|
||||||
server.events.on('log', (event, tags) => {
|
server.events.on('log', (event, tags) => {
|
||||||
if (tags.error) {
|
if (tags.error) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(event);
|
console.log(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -58,7 +58,6 @@ module.exports = async ({ PORT, BASE_URL }) => {
|
|||||||
const { tags } = event;
|
const { tags } = event;
|
||||||
if (tags.includes('error') && event.data && event.data.errors) {
|
if (tags.includes('error') && event.data && event.data.errors) {
|
||||||
event.data.errors.forEach(error => {
|
event.data.errors.forEach(error => {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
require('../.env.js');
|
|
||||||
|
|
||||||
const Main = require('apr-main');
|
|
||||||
const CloudApiGql = require('cloudapi-gql');
|
|
||||||
const Tsg = require('tsg-graphql');
|
|
||||||
const Graphi = require('graphi');
|
|
||||||
const Url = require('url');
|
|
||||||
|
|
||||||
const Server = require('./server');
|
|
||||||
const Ui = require('my-joy-service-groups');
|
|
||||||
|
|
||||||
const {
|
|
||||||
PORT = 4004,
|
|
||||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
|
||||||
PREFIX = 'service-groups',
|
|
||||||
DC_NAME,
|
|
||||||
TSG_URL = 'http://0.0.0.0:3000',
|
|
||||||
SDC_URL,
|
|
||||||
SDC_KEY_PATH,
|
|
||||||
SDC_ACCOUNT,
|
|
||||||
SDC_KEY_ID
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
|
||||||
const keyPath = SDC_KEY_PATH;
|
|
||||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
|
||||||
|
|
||||||
Main(async () => {
|
|
||||||
const server = await Server({
|
|
||||||
PORT,
|
|
||||||
BASE_URL
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.register([
|
|
||||||
{
|
|
||||||
plugin: Graphi,
|
|
||||||
options: {
|
|
||||||
graphqlPath: '/graphql',
|
|
||||||
graphiqlPath: '/graphiql',
|
|
||||||
authStrategy: 'sso'
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Tsg,
|
|
||||||
options: {
|
|
||||||
authStrategy: 'sso',
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl: TSG_URL,
|
|
||||||
dcName
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: CloudApiGql,
|
|
||||||
options: {
|
|
||||||
authStrategy: 'sso',
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl: SDC_URL,
|
|
||||||
dcName
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Ui
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
await server.start();
|
|
||||||
});
|
|
@ -1,78 +0,0 @@
|
|||||||
require('../.env.js');
|
|
||||||
|
|
||||||
const Main = require('apr-main');
|
|
||||||
const CloudApiGql = require('cloudapi-gql');
|
|
||||||
const Tsg = require('tsg-graphql');
|
|
||||||
const Graphi = require('graphi');
|
|
||||||
const Url = require('url');
|
|
||||||
|
|
||||||
const Server = require('./server');
|
|
||||||
const Ui = require('my-joy-templates');
|
|
||||||
|
|
||||||
const {
|
|
||||||
PORT = 4005,
|
|
||||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
|
||||||
PREFIX = 'templates',
|
|
||||||
DC_NAME,
|
|
||||||
TSG_URL = 'http://0.0.0.0:3000',
|
|
||||||
SDC_URL,
|
|
||||||
SDC_KEY_PATH,
|
|
||||||
SDC_ACCOUNT,
|
|
||||||
SDC_KEY_ID
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
|
||||||
const keyPath = SDC_KEY_PATH;
|
|
||||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
|
||||||
|
|
||||||
Main(async () => {
|
|
||||||
const server = await Server({
|
|
||||||
PORT,
|
|
||||||
BASE_URL
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.register([
|
|
||||||
{
|
|
||||||
plugin: Graphi,
|
|
||||||
options: {
|
|
||||||
graphqlPath: '/graphql',
|
|
||||||
graphiqlPath: '/graphiql',
|
|
||||||
authStrategy: 'sso'
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Tsg,
|
|
||||||
options: {
|
|
||||||
authStrategy: 'sso',
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl: TSG_URL,
|
|
||||||
dcName
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: CloudApiGql,
|
|
||||||
options: {
|
|
||||||
authStrategy: 'sso',
|
|
||||||
keyPath,
|
|
||||||
keyId,
|
|
||||||
apiBaseUrl: SDC_URL,
|
|
||||||
dcName
|
|
||||||
},
|
|
||||||
routes: {
|
|
||||||
prefix: `/${PREFIX}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: Ui
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
await server.start();
|
|
||||||
});
|
|
@ -4,16 +4,7 @@ module.exports = {
|
|||||||
'scope-enum': [
|
'scope-enum': [
|
||||||
2,
|
2,
|
||||||
'always',
|
'always',
|
||||||
[
|
['ui-toolkit', 'icons', 'instances', 'navigation', 'bundle', 'images']
|
||||||
'ui-toolkit',
|
|
||||||
'icons',
|
|
||||||
'instances',
|
|
||||||
'navigation',
|
|
||||||
'bundle',
|
|
||||||
'images',
|
|
||||||
'sg',
|
|
||||||
'templates'
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Margin } from 'styled-components-spacing';
|
|
||||||
import { NavLink } from 'react-router-dom';
|
|
||||||
import forceArray from 'force-array';
|
|
||||||
|
|
||||||
import {
|
|
||||||
SectionList,
|
|
||||||
SectionListItem,
|
|
||||||
SectionListAnchor,
|
|
||||||
ViewContainer
|
|
||||||
} from 'joyent-ui-toolkit';
|
|
||||||
|
|
||||||
const getMenuItems = (links = []) =>
|
|
||||||
links.map(({ pathname, name }) => (
|
|
||||||
<SectionListItem key={pathname}>
|
|
||||||
<SectionListAnchor to={pathname} component={NavLink}>
|
|
||||||
{name}
|
|
||||||
</SectionListAnchor>
|
|
||||||
</SectionListItem>
|
|
||||||
));
|
|
||||||
|
|
||||||
const Menu = ({ links = [] }) => {
|
|
||||||
const _links = forceArray(links);
|
|
||||||
|
|
||||||
if (!_links.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewContainer plain>
|
|
||||||
<Margin bottom="5" top="1">
|
|
||||||
<SectionList>{getMenuItems(_links)}</SectionList>
|
|
||||||
</Margin>
|
|
||||||
</ViewContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Menu;
|
|
@ -1,28 +0,0 @@
|
|||||||
const React = require('react');
|
|
||||||
|
|
||||||
module.exports = ({
|
|
||||||
htmlAttrs = {},
|
|
||||||
bodyAttrs = {},
|
|
||||||
head = [],
|
|
||||||
children = null
|
|
||||||
}) => (
|
|
||||||
<html {...htmlAttrs}>
|
|
||||||
<head>
|
|
||||||
<meta charSet="utf-8" />
|
|
||||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
|
||||||
/>
|
|
||||||
<meta name="theme-color" content="#1E313B" />
|
|
||||||
|
|
||||||
{head}
|
|
||||||
</head>
|
|
||||||
<body {...bodyAttrs}>
|
|
||||||
<div id="header" />
|
|
||||||
{children ? null : <div id="root" />}
|
|
||||||
{children}
|
|
||||||
<script src="/navigation/static/main.js" />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
@ -1,33 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import { HelmetProvider } from 'react-helmet-async';
|
|
||||||
import { ThemeProvider } from 'styled-components';
|
|
||||||
import { Provider as ReduxProvider } from 'react-redux';
|
|
||||||
import { ApolloProvider } from 'react-apollo';
|
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
import isFunction from 'lodash.isfunction';
|
|
||||||
import isFinite from 'lodash.isfinite';
|
|
||||||
|
|
||||||
import theme from '@state/theme';
|
|
||||||
import createStore from '@state/redux-store';
|
|
||||||
import createClient from '@state/apollo-client';
|
|
||||||
import App from './app';
|
|
||||||
|
|
||||||
if (!isFunction(Number.isFinite)) {
|
|
||||||
Number.isFinite = isFinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.hydrate(
|
|
||||||
<ApolloProvider client={createClient()}>
|
|
||||||
<ThemeProvider theme={theme}>
|
|
||||||
<ReduxProvider store={createStore()}>
|
|
||||||
<BrowserRouter>
|
|
||||||
<HelmetProvider context={{}}>
|
|
||||||
<App />
|
|
||||||
</HelmetProvider>
|
|
||||||
</BrowserRouter>
|
|
||||||
</ReduxProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</ApolloProvider>,
|
|
||||||
document.getElementById('root')
|
|
||||||
);
|
|
@ -1,42 +0,0 @@
|
|||||||
import { ApolloClient } from 'apollo-client';
|
|
||||||
import { HttpLink } from 'apollo-link-http';
|
|
||||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
|
||||||
import fetch from 'cross-fetch';
|
|
||||||
import get from 'lodash.get';
|
|
||||||
|
|
||||||
import global from './global';
|
|
||||||
|
|
||||||
const {
|
|
||||||
REACT_APP_GQL_PORT = global.port,
|
|
||||||
REACT_APP_GQL_PROTOCOL = global.protocol,
|
|
||||||
REACT_APP_GQL_HOSTNAME = global.hostname
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
const PORT = REACT_APP_GQL_PORT ? `:${REACT_APP_GQL_PORT}` : '';
|
|
||||||
const URI = `${REACT_APP_GQL_PROTOCOL}://${REACT_APP_GQL_HOSTNAME}${PORT}/images/graphql`;
|
|
||||||
|
|
||||||
export default (opts = {}, request = {}) => {
|
|
||||||
const host = get(request, 'raw.req.headers.host', '');
|
|
||||||
|
|
||||||
let cache = new InMemoryCache();
|
|
||||||
|
|
||||||
if (global.__APOLLO_STATE__) {
|
|
||||||
cache = cache.restore(global.__APOLLO_STATE__);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ApolloClient({
|
|
||||||
cache,
|
|
||||||
link: new HttpLink({
|
|
||||||
uri: host ? `${REACT_APP_GQL_PROTOCOL}//${host}/images/graphql` : URI,
|
|
||||||
credentials: 'same-origin',
|
|
||||||
fetch,
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-Token': global.cookie.replace(
|
|
||||||
/(?:(?:^|.*;\s*)crumb\s*=\s*([^;]*).*$)|^.*$/,
|
|
||||||
'$1'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
...opts
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,29 +0,0 @@
|
|||||||
import { canUseDOM } from 'exenv';
|
|
||||||
import queryString from 'query-string';
|
|
||||||
|
|
||||||
const { NODE_ENV = 'development' } = process.env;
|
|
||||||
|
|
||||||
export const Global = () => {
|
|
||||||
if (!canUseDOM) {
|
|
||||||
return {
|
|
||||||
protocol: NODE_ENV === 'development' ? 'http:' : 'https:',
|
|
||||||
cookie: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
port: window.location.port,
|
|
||||||
protocol: window.location.protocol.replace(/:$/, ''),
|
|
||||||
hostname: window.location.hostname,
|
|
||||||
pathname: window.location.pathname,
|
|
||||||
origin: window.location.origin,
|
|
||||||
cookie: document.cookie || '',
|
|
||||||
search: window.location.search,
|
|
||||||
query: queryString.parse(window.location.search || ''),
|
|
||||||
__REDUX_DEVTOOLS_EXTENSION__: window.__REDUX_DEVTOOLS_EXTENSION__,
|
|
||||||
__APOLLO_STATE__: window.__APOLLO_STATE__,
|
|
||||||
__REDUX_STATE__: window.__REDUX_STATE__
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Global();
|
|
@ -1,133 +0,0 @@
|
|||||||
const Boom = require('boom');
|
|
||||||
const Inert = require('inert');
|
|
||||||
const Path = require('path');
|
|
||||||
const RenderReact = require('hapi-render-react');
|
|
||||||
const Intercept = require('apr-intercept');
|
|
||||||
const Fs = require('mz/fs');
|
|
||||||
|
|
||||||
const { NAMESPACE = 'instances', NODE_ENV = 'development' } = process.env;
|
|
||||||
|
|
||||||
exports.register = async server => {
|
|
||||||
let manifest = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
manifest = require('../build/asset-manifest.json');
|
|
||||||
} catch (err) {
|
|
||||||
if (NODE_ENV === 'production') {
|
|
||||||
throw err;
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const relativeTo = Path.join(__dirname, 'app');
|
|
||||||
const buildRoot = Path.join(__dirname, '../build');
|
|
||||||
const buildStatic = Path.join(buildRoot, `${NAMESPACE}`);
|
|
||||||
const publicRoot = Path.join(__dirname, `../public/static/`);
|
|
||||||
|
|
||||||
await server.register([
|
|
||||||
{
|
|
||||||
plugin: Inert
|
|
||||||
},
|
|
||||||
{
|
|
||||||
plugin: RenderReact
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
server.route([
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: `/${NAMESPACE}/service-worker.js`,
|
|
||||||
config: {
|
|
||||||
auth: false,
|
|
||||||
handler: {
|
|
||||||
file: {
|
|
||||||
path: Path.join(__dirname, '../build/service-worker.js')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: `/${NAMESPACE}/favicon.ico`,
|
|
||||||
config: {
|
|
||||||
auth: false,
|
|
||||||
handler: {
|
|
||||||
file: {
|
|
||||||
path: Path.join(__dirname, '../build/favicon.ico')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: 'GET',
|
|
||||||
path: `/${NAMESPACE}/static/{rest*}`,
|
|
||||||
config: {
|
|
||||||
auth: false
|
|
||||||
},
|
|
||||||
handler: async (request, h) => {
|
|
||||||
const { params } = request;
|
|
||||||
const { rest } = params;
|
|
||||||
|
|
||||||
if (!rest) {
|
|
||||||
return Boom.notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const publicPathname = Path.join(publicRoot, rest);
|
|
||||||
const [err1] = await Intercept(
|
|
||||||
Fs.access(publicPathname, Fs.constants.R_OK)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!err1) {
|
|
||||||
return h.file(publicPathname, {
|
|
||||||
confine: publicRoot
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildPathname = Path.join(buildStatic, 'static', rest);
|
|
||||||
const [err2] = await Intercept(
|
|
||||||
Fs.access(buildPathname, Fs.constants.R_OK)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!err2) {
|
|
||||||
return h.file(buildPathname, {
|
|
||||||
confine: buildStatic
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename = manifest[rest];
|
|
||||||
if (!filename) {
|
|
||||||
return Boom.notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildMapPathname = Path.join(buildRoot, filename);
|
|
||||||
return h.file(buildMapPathname, {
|
|
||||||
confine: buildStatic
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: '*',
|
|
||||||
path: `/${NAMESPACE}/~server-error`,
|
|
||||||
handler: {
|
|
||||||
view: {
|
|
||||||
name: 'server-error',
|
|
||||||
relativeTo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
method: '*',
|
|
||||||
path: `/${NAMESPACE}/{path*}`,
|
|
||||||
handler: {
|
|
||||||
view: {
|
|
||||||
name: 'app',
|
|
||||||
relativeTo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.pkg = require('../package.json');
|
|
@ -1,54 +0,0 @@
|
|||||||
const get = require('lodash.get');
|
|
||||||
const Document = require('hapi-render-react-joyent-document');
|
|
||||||
const url = require('url');
|
|
||||||
|
|
||||||
const { theme } = require('joyent-ui-toolkit');
|
|
||||||
const { default: createClient } = require('./state/apollo-client');
|
|
||||||
const { default: createStore } = require('./state/redux-store');
|
|
||||||
|
|
||||||
const assets = require('../../build/asset-manifest.json');
|
|
||||||
|
|
||||||
const { NODE_ENV = 'development' } = process.env;
|
|
||||||
|
|
||||||
const getState = request => {
|
|
||||||
const { req } = request.raw;
|
|
||||||
const { headers } = req;
|
|
||||||
const { host } = headers;
|
|
||||||
|
|
||||||
const protocol = NODE_ENV === 'development' ? 'http:' : 'https:';
|
|
||||||
const _font = get(theme, 'font.href', () => '');
|
|
||||||
const _mono = get(theme, 'monoFont.href', () => '');
|
|
||||||
const _addr = url.parse(`${protocol}//${host}`);
|
|
||||||
|
|
||||||
const _theme = Object.assign({}, theme, {
|
|
||||||
font: Object.assign({}, theme.font, {
|
|
||||||
href: () =>
|
|
||||||
_font(
|
|
||||||
Object.assign(_addr, {
|
|
||||||
namespace: 'instances'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
monoFont: Object.assign({}, theme.monoFont, {
|
|
||||||
href: () =>
|
|
||||||
_mono(
|
|
||||||
Object.assign(_addr, {
|
|
||||||
namespace: 'instances'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
theme: _theme,
|
|
||||||
createClient,
|
|
||||||
createStore
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Document({
|
|
||||||
namespace: 'instances/',
|
|
||||||
assets,
|
|
||||||
Html: require('./html'),
|
|
||||||
getState
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Helmet from 'react-helmet-async';
|
|
||||||
|
|
||||||
import { RootContainer } from 'joyent-ui-toolkit';
|
|
||||||
import Routes from '@root/routes';
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<RootContainer>
|
|
||||||
<Helmet>
|
|
||||||
<title>Instances</title>
|
|
||||||
</Helmet>
|
|
||||||
<Routes />
|
|
||||||
</RootContainer>
|
|
||||||
);
|
|
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB |
@ -1,23 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { P as BaseP } from 'joyent-ui-toolkit';
|
|
||||||
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const P = styled(BaseP)`
|
|
||||||
font-weight: 200;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default ({ href = '', children }) => (
|
|
||||||
<Row>
|
|
||||||
<Col xs="12" sm="7">
|
|
||||||
<P>
|
|
||||||
{children}{' '}
|
|
||||||
{href ? (
|
|
||||||
<a target="__blank" href={href} rel="noopener noreferrer">
|
|
||||||
Read the docs
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</P>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
@ -1,173 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
|
|
||||||
import { Margin } from 'styled-components-spacing';
|
|
||||||
import Flex from 'styled-flex-component';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
StickyFooter,
|
|
||||||
QueryBreakpoints,
|
|
||||||
StartIcon,
|
|
||||||
StopIcon,
|
|
||||||
ResetIcon,
|
|
||||||
DeleteIcon
|
|
||||||
} from 'joyent-ui-toolkit';
|
|
||||||
|
|
||||||
const { SmallOnly, Medium } = QueryBreakpoints;
|
|
||||||
|
|
||||||
export default ({
|
|
||||||
submitting = false,
|
|
||||||
statuses = {},
|
|
||||||
allowedActions = {},
|
|
||||||
onStart,
|
|
||||||
onStop,
|
|
||||||
onReboot,
|
|
||||||
onRemove
|
|
||||||
}) => (
|
|
||||||
<StickyFooter fill="disabled" fixed bottom>
|
|
||||||
<Row between="xs" middle="xs">
|
|
||||||
<Col xs="7">
|
|
||||||
<Flex>
|
|
||||||
{onStart && [
|
|
||||||
<SmallOnly key="small-only">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onStart}
|
|
||||||
disabled={submitting || !allowedActions.start}
|
|
||||||
loading={submitting && statuses.starting}
|
|
||||||
secondary
|
|
||||||
small
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<StartIcon disabled={submitting || !allowedActions.start} />
|
|
||||||
</Button>
|
|
||||||
</SmallOnly>,
|
|
||||||
<Margin right="1">
|
|
||||||
<Medium key="medium">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onStart}
|
|
||||||
disabled={submitting || !allowedActions.start}
|
|
||||||
loading={submitting && statuses.starting}
|
|
||||||
secondary
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<Margin right="1">
|
|
||||||
<StartIcon disabled={submitting || !allowedActions.start} />
|
|
||||||
</Margin>
|
|
||||||
<span>Start</span>
|
|
||||||
</Button>
|
|
||||||
</Medium>
|
|
||||||
</Margin>
|
|
||||||
]}
|
|
||||||
{onStop && [
|
|
||||||
<SmallOnly key="small-only">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onStop}
|
|
||||||
disabled={submitting || !allowedActions.stop}
|
|
||||||
loading={submitting && statuses.stopping}
|
|
||||||
secondary
|
|
||||||
small
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<StopIcon disabled={submitting || !allowedActions.stop} />
|
|
||||||
</Button>
|
|
||||||
</SmallOnly>,
|
|
||||||
<Margin right="1">
|
|
||||||
<Medium key="medium">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onStop}
|
|
||||||
disabled={submitting || !allowedActions.stop}
|
|
||||||
loading={submitting && statuses.stopping}
|
|
||||||
secondary
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<Margin right="1">
|
|
||||||
<StopIcon disabled={submitting || !allowedActions.stop} />
|
|
||||||
</Margin>
|
|
||||||
<span>Stop</span>
|
|
||||||
</Button>
|
|
||||||
</Medium>
|
|
||||||
</Margin>
|
|
||||||
]}
|
|
||||||
{onReboot && [
|
|
||||||
<SmallOnly key="small-only">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onReboot}
|
|
||||||
disabled={submitting || !allowedActions.reboot}
|
|
||||||
loading={submitting && statuses.rebooting}
|
|
||||||
secondary
|
|
||||||
small
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<ResetIcon disabled={submitting || !allowedActions.reboot} />
|
|
||||||
</Button>
|
|
||||||
</SmallOnly>,
|
|
||||||
<Margin right="1">
|
|
||||||
<Medium key="medium">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onReboot}
|
|
||||||
disabled={submitting || !allowedActions.reboot}
|
|
||||||
loading={submitting && statuses.rebooting}
|
|
||||||
secondary
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<ResetIcon disabled={submitting || !allowedActions.reboot} />
|
|
||||||
<span>Reboot</span>
|
|
||||||
</Button>
|
|
||||||
</Medium>
|
|
||||||
</Margin>
|
|
||||||
]}
|
|
||||||
</Flex>
|
|
||||||
</Col>
|
|
||||||
{onRemove && (
|
|
||||||
<Col xs="5">
|
|
||||||
<SmallOnly key="small-only">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onRemove}
|
|
||||||
disabled={submitting || !allowedActions.remove}
|
|
||||||
loading={submitting && statuses.removing}
|
|
||||||
secondary
|
|
||||||
error
|
|
||||||
right
|
|
||||||
small
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<DeleteIcon
|
|
||||||
disabled={submitting}
|
|
||||||
fill={submitting || !allowedActions.remove ? undefined : 'red'}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</SmallOnly>
|
|
||||||
<Medium key="medium">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={onRemove}
|
|
||||||
disabled={submitting || !allowedActions.remove}
|
|
||||||
loading={submitting && statuses.removing}
|
|
||||||
error
|
|
||||||
secondary
|
|
||||||
right
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<Margin right="1">
|
|
||||||
<DeleteIcon
|
|
||||||
disabled={submitting || !allowedActions.remove}
|
|
||||||
fill={
|
|
||||||
submitting || !allowedActions.remove ? undefined : 'red'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<span>Remove</span>
|
|
||||||
</Button>
|
|
||||||
</Medium>
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</StickyFooter>
|
|
||||||
);
|
|
@ -1,394 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
|
|
||||||
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
|
|
||||||
import Flex, { FlexItem } from 'styled-flex-component';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Margin, Padding } from 'styled-components-spacing';
|
|
||||||
import titleCase from 'title-case';
|
|
||||||
import get from 'lodash.get';
|
|
||||||
import remcalc from 'remcalc';
|
|
||||||
import { Field } from 'redux-form';
|
|
||||||
import { ValueBreakpoints as breakpoints } from 'joyent-ui-toolkit';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardOutlet,
|
|
||||||
Divider,
|
|
||||||
Button,
|
|
||||||
ButtonGroup,
|
|
||||||
PopoverButton,
|
|
||||||
PopoverItem as BasePopoverItem,
|
|
||||||
H2 as BaseH2,
|
|
||||||
Label as BaseLabel,
|
|
||||||
CopiableField,
|
|
||||||
QueryBreakpoints,
|
|
||||||
DotIcon,
|
|
||||||
DeleteIcon,
|
|
||||||
StartIcon,
|
|
||||||
StopIcon,
|
|
||||||
EditIcon,
|
|
||||||
Input,
|
|
||||||
FormMeta,
|
|
||||||
FormGroup
|
|
||||||
} from 'joyent-ui-toolkit';
|
|
||||||
|
|
||||||
import GLOBAL from '@state/global';
|
|
||||||
|
|
||||||
const { SmallOnly, Medium } = QueryBreakpoints;
|
|
||||||
|
|
||||||
const stateColor = {
|
|
||||||
PROVISIONING: 'primary',
|
|
||||||
RUNNING: 'green',
|
|
||||||
STOPPING: 'grey',
|
|
||||||
STOPPED: 'grey',
|
|
||||||
DELETED: 'secondaryActive',
|
|
||||||
FAILED: 'red'
|
|
||||||
};
|
|
||||||
|
|
||||||
const Label = styled(BaseLabel)`
|
|
||||||
font-weight: 200;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GreyLabel = styled(Label)`
|
|
||||||
opacity: 0.5;
|
|
||||||
padding-right: ${remcalc(3)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TrimedLabel = styled(Label)`
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Actionable = styled(Margin)`
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const VerticalDivider = styled.div`
|
|
||||||
width: ${remcalc(1)};
|
|
||||||
background: ${props => props.theme.grey};
|
|
||||||
height: ${remcalc(24)};
|
|
||||||
display: flex;
|
|
||||||
align-self: flex-end;
|
|
||||||
margin: 0 ${remcalc(12)};
|
|
||||||
|
|
||||||
@media (max-width: ${remcalc(breakpoints.small.upper)}) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const H2 = styled(BaseH2)`
|
|
||||||
margin: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PopoverItem = styled(BasePopoverItem)`
|
|
||||||
padding-bottom: ${remcalc(11)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Meta = ({
|
|
||||||
created,
|
|
||||||
updated,
|
|
||||||
state,
|
|
||||||
brand,
|
|
||||||
image,
|
|
||||||
editingName,
|
|
||||||
handleSubmit,
|
|
||||||
editName,
|
|
||||||
disabled,
|
|
||||||
submitting,
|
|
||||||
...instance
|
|
||||||
}) => [
|
|
||||||
<Row middle="xs">
|
|
||||||
<Col xs="12">
|
|
||||||
<Margin bottom="1">
|
|
||||||
<H2>
|
|
||||||
{editingName ? (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<Flex alignStart>
|
|
||||||
<FormGroup name="name" field={Field}>
|
|
||||||
<Input
|
|
||||||
onBlur={null}
|
|
||||||
type="text"
|
|
||||||
placeholder={instance.name}
|
|
||||||
disabled={disabled || submitting}
|
|
||||||
/>
|
|
||||||
<FormMeta />
|
|
||||||
</FormGroup>
|
|
||||||
<Margin left="1">
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
disabled={submitting}
|
|
||||||
loading={submitting}
|
|
||||||
inline
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Margin>
|
|
||||||
</Flex>
|
|
||||||
</form>
|
|
||||||
) : (
|
|
||||||
<Flex>
|
|
||||||
{instance.name}
|
|
||||||
<Actionable left="2" onClick={editName}>
|
|
||||||
<EditIcon />
|
|
||||||
</Actionable>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</H2>
|
|
||||||
</Margin>
|
|
||||||
</Col>
|
|
||||||
</Row>,
|
|
||||||
<Margin vertical="1">
|
|
||||||
<Flex>
|
|
||||||
<TrimedLabel>
|
|
||||||
{image && image.name ? titleCase(image.name) : 'Custom Image'}
|
|
||||||
</TrimedLabel>
|
|
||||||
<VerticalDivider />
|
|
||||||
<TrimedLabel>
|
|
||||||
{brand === 'LX'
|
|
||||||
? 'Infrastructure container'
|
|
||||||
: 'Hardware virtual machine'}
|
|
||||||
</TrimedLabel>
|
|
||||||
<VerticalDivider />
|
|
||||||
<TrimedLabel>{(instance.package || {}).name}</TrimedLabel>
|
|
||||||
<VerticalDivider />
|
|
||||||
<Flex>
|
|
||||||
<DotIcon
|
|
||||||
right={remcalc(6)}
|
|
||||||
size={remcalc(15)}
|
|
||||||
color={stateColor[state]}
|
|
||||||
/>
|
|
||||||
{titleCase(state)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Margin top="1">
|
|
||||||
<Flex>
|
|
||||||
<Flex>
|
|
||||||
<GreyLabel>Created: </GreyLabel>
|
|
||||||
<Label> {distanceInWordsToNow(created)} ago</Label>
|
|
||||||
</Flex>
|
|
||||||
<VerticalDivider />
|
|
||||||
<Flex>
|
|
||||||
<GreyLabel>Updated: </GreyLabel>
|
|
||||||
<Label> {distanceInWordsToNow(updated)} ago</Label>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Margin>
|
|
||||||
</Margin>
|
|
||||||
];
|
|
||||||
|
|
||||||
export default ({
|
|
||||||
instance = {},
|
|
||||||
starting = false,
|
|
||||||
stopping = false,
|
|
||||||
rebooting = false,
|
|
||||||
removing = false,
|
|
||||||
onAction,
|
|
||||||
...props
|
|
||||||
}) => (
|
|
||||||
<Row>
|
|
||||||
<Col xs="12" sm="12" md="9">
|
|
||||||
<Card>
|
|
||||||
<CardOutlet>
|
|
||||||
<Padding all="5">
|
|
||||||
<Meta {...instance} {...props} />
|
|
||||||
<Margin top="3">
|
|
||||||
<Row between="xs">
|
|
||||||
<Col xs="9">
|
|
||||||
<Flex>
|
|
||||||
<FlexItem>
|
|
||||||
<Margin right="1">
|
|
||||||
<ButtonGroup>
|
|
||||||
{instance.state === 'STOPPED' ? (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
loading={starting}
|
|
||||||
disabled={instance.state !== 'STOPPED'}
|
|
||||||
onClick={() => onAction('start')}
|
|
||||||
secondary
|
|
||||||
bold
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<Margin right="2">
|
|
||||||
<StartIcon
|
|
||||||
disabled={instance.state !== 'STOPPED'}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<span>Start</span>
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
loading={stopping}
|
|
||||||
disabled={instance.state !== 'RUNNING'}
|
|
||||||
onClick={() => onAction('stop')}
|
|
||||||
secondary
|
|
||||||
bold
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
<Margin right="2">
|
|
||||||
<StopIcon
|
|
||||||
disabled={instance.state !== 'RUNNING'}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<span>Stop</span>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<PopoverButton secondary>
|
|
||||||
<PopoverItem
|
|
||||||
disabled={instance.state === 'RUNNING'}
|
|
||||||
onClick={() =>
|
|
||||||
instance.state === 'RUNNING'
|
|
||||||
? null
|
|
||||||
: onAction('start')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
</PopoverItem>
|
|
||||||
<PopoverItem
|
|
||||||
disabled={instance.state === 'STOPPED'}
|
|
||||||
onClick={() =>
|
|
||||||
instance.state === 'STOPPED'
|
|
||||||
? null
|
|
||||||
: onAction('reboot')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Restart
|
|
||||||
</PopoverItem>
|
|
||||||
<PopoverItem
|
|
||||||
disabled={instance.state === 'STOPPED'}
|
|
||||||
onClick={() =>
|
|
||||||
instance.state === 'STOPPED'
|
|
||||||
? null
|
|
||||||
: onAction('stop')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
</PopoverItem>
|
|
||||||
</PopoverButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
</Margin>
|
|
||||||
</FlexItem>
|
|
||||||
<FlexItem>
|
|
||||||
<Button
|
|
||||||
href={`${GLOBAL.origin}/images/~create/${instance.id}`}
|
|
||||||
target="__blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
secondary
|
|
||||||
bold
|
|
||||||
icon
|
|
||||||
>
|
|
||||||
Create Image
|
|
||||||
</Button>
|
|
||||||
</FlexItem>
|
|
||||||
</Flex>
|
|
||||||
</Col>
|
|
||||||
<Col xs="3">
|
|
||||||
<SmallOnly>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
loading={removing}
|
|
||||||
disabled={
|
|
||||||
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
|
|
||||||
}
|
|
||||||
onClick={() => onAction('remove')}
|
|
||||||
secondary
|
|
||||||
small
|
|
||||||
right
|
|
||||||
icon
|
|
||||||
error
|
|
||||||
>
|
|
||||||
<Margin right="2">
|
|
||||||
<DeleteIcon
|
|
||||||
fill="red"
|
|
||||||
disabled={
|
|
||||||
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
</Button>
|
|
||||||
</SmallOnly>
|
|
||||||
<Medium>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
loading={removing}
|
|
||||||
disabled={
|
|
||||||
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
|
|
||||||
}
|
|
||||||
onClick={() => onAction('remove')}
|
|
||||||
secondary
|
|
||||||
bold
|
|
||||||
right
|
|
||||||
icon
|
|
||||||
error
|
|
||||||
>
|
|
||||||
<Margin right="2">
|
|
||||||
<DeleteIcon
|
|
||||||
fill={
|
|
||||||
['RUNNING', 'STOPPED'].indexOf(instance.state) >= 0
|
|
||||||
? 'red'
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<span>Delete</span>
|
|
||||||
</Button>
|
|
||||||
</Medium>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5" top="3">
|
|
||||||
<Divider height={1} />
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField
|
|
||||||
text={(instance.id || '').split('-')[0]}
|
|
||||||
label="Short ID"
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField text={instance.id} label="ID" />
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField text={instance.compute_node} label="CN UUID" />
|
|
||||||
</Margin>
|
|
||||||
{instance.image &&
|
|
||||||
instance.image.id && (
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField text={instance.image.id} label="Image UUID" />
|
|
||||||
</Margin>
|
|
||||||
)}
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField
|
|
||||||
text={`ssh root@${instance.primary_ip}`}
|
|
||||||
label="Login"
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
{get(instance, 'ips.public', []).map((ip, i, ips) => (
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField
|
|
||||||
key={`public-${i}`}
|
|
||||||
label={`Public IP address ${ips.length > 1 ? i + 1 : ''}`}
|
|
||||||
text={ip}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
))}
|
|
||||||
{get(instance, 'ips.private', []).map((ip, i, ips) => (
|
|
||||||
<Margin bottom="3">
|
|
||||||
<CopiableField
|
|
||||||
key={`private-${i}`}
|
|
||||||
noMargin={i === ips.length - 1}
|
|
||||||
label={`Private IP address ${ips.length > 1 ? i + 1 : ''}`}
|
|
||||||
text={ip}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
))}
|
|
||||||
</Padding>
|
|
||||||
</CardOutlet>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
@ -1,353 +0,0 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
|
||||||
import { Margin, Padding } from 'styled-components-spacing';
|
|
||||||
import { graphql, compose } from 'react-apollo';
|
|
||||||
import ReduxForm from 'declarative-redux-form';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { SubmissionError, destroy } from 'redux-form';
|
|
||||||
import { set, destroyAll } from 'react-redux-values';
|
|
||||||
import Flex, { FlexItem } from 'styled-flex-component';
|
|
||||||
import intercept from 'apr-intercept';
|
|
||||||
import get from 'lodash.get';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Name,
|
|
||||||
Image,
|
|
||||||
Package,
|
|
||||||
Networks,
|
|
||||||
Tags,
|
|
||||||
Metadata,
|
|
||||||
UserScript,
|
|
||||||
Firewall,
|
|
||||||
CNS,
|
|
||||||
Affinity,
|
|
||||||
generatePayload,
|
|
||||||
Footer
|
|
||||||
} from 'joyent-ui-instance-steps';
|
|
||||||
|
|
||||||
import { Provider as ResourceSteps } from 'joyent-ui-resource-step';
|
|
||||||
import { Anchor, H3, ViewContainer, Button } from 'joyent-ui-toolkit';
|
|
||||||
|
|
||||||
import { Forms, Values } from '@root/constants';
|
|
||||||
import parseError from '@state/parse-error';
|
|
||||||
import CreateInstanceMutation from '@graphql/create-instance.gql';
|
|
||||||
|
|
||||||
const { IC_F } = Forms;
|
|
||||||
const { IC_SHOW_CLI } = Values;
|
|
||||||
|
|
||||||
const names = {
|
|
||||||
name: 'IC_NAME',
|
|
||||||
image: 'IC_IMAGE',
|
|
||||||
package: 'IC_PACKAGE',
|
|
||||||
networks: 'IC_NETWORKS',
|
|
||||||
tags: 'IC_TAGS',
|
|
||||||
metadata: 'IC_METADATA',
|
|
||||||
'user-script': 'IC_USERSCRIPT',
|
|
||||||
firewall: 'IC_FIREWALL',
|
|
||||||
cns: 'IC_CNS',
|
|
||||||
affinity: 'IC_AFFINITY'
|
|
||||||
};
|
|
||||||
|
|
||||||
class CreateInstance extends Component {
|
|
||||||
constructor(...args) {
|
|
||||||
super(...args);
|
|
||||||
this.isValids = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsValid = name => ref => {
|
|
||||||
if (!ref) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { isValid } = ref;
|
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isValids = Object.assign({}, this.isValids, {
|
|
||||||
[name]: isValid
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
isFormValid = () => {
|
|
||||||
const { steps } = this.props;
|
|
||||||
|
|
||||||
return Object.keys(this.isValids).every(name =>
|
|
||||||
this.isValids[name](steps[name] || {})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
isStepValid = step => {
|
|
||||||
const { steps } = this.props;
|
|
||||||
const fn = this.isValids[step];
|
|
||||||
const values = steps[step];
|
|
||||||
|
|
||||||
if (!fn || !values) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn(values);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
match,
|
|
||||||
steps,
|
|
||||||
showCli = false,
|
|
||||||
handleDefocus,
|
|
||||||
handleToggleShowCli,
|
|
||||||
handleSubmit,
|
|
||||||
disabled
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { params } = match;
|
|
||||||
const { step } = params;
|
|
||||||
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
image,
|
|
||||||
package: pkg,
|
|
||||||
networks,
|
|
||||||
tags,
|
|
||||||
metadata,
|
|
||||||
firewall,
|
|
||||||
cns,
|
|
||||||
affinity
|
|
||||||
} = steps;
|
|
||||||
|
|
||||||
const Mask = styled.div`
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.25);
|
|
||||||
position: absolute;
|
|
||||||
display: ${showCli ? 'block' : 'none'};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewContainer main>
|
|
||||||
<Margin top="5">
|
|
||||||
<H3>Create Instance</H3>
|
|
||||||
</Margin>
|
|
||||||
<Padding top="5">
|
|
||||||
<ResourceSteps namespace="instances/~create">
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Name
|
|
||||||
ref={this.setIsValid('name')}
|
|
||||||
expanded={step === 'name'}
|
|
||||||
next="image"
|
|
||||||
saved={get(steps, 'name.name', false)}
|
|
||||||
onDefocus={handleDefocus('name')}
|
|
||||||
preview={name}
|
|
||||||
isValid={this.isStepValid('name')}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Image
|
|
||||||
ref={this.setIsValid('image')}
|
|
||||||
expanded={step === 'image'}
|
|
||||||
next="package"
|
|
||||||
saved={steps.image && steps.image.id}
|
|
||||||
onDefocus={handleDefocus('image')}
|
|
||||||
preview={image}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Package
|
|
||||||
ref={this.setIsValid('package')}
|
|
||||||
expanded={step === 'package'}
|
|
||||||
next="networks"
|
|
||||||
saved={steps.package}
|
|
||||||
onDefocus={handleDefocus('package')}
|
|
||||||
preview={pkg}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Networks
|
|
||||||
ref={this.setIsValid('networks')}
|
|
||||||
expanded={step === 'networks'}
|
|
||||||
next="tags"
|
|
||||||
saved={steps.networks}
|
|
||||||
onDefocus={handleDefocus('networks')}
|
|
||||||
preview={networks}
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Tags
|
|
||||||
ref={this.setIsValid('tags')}
|
|
||||||
expanded={step === 'tags'}
|
|
||||||
next="metadata"
|
|
||||||
saved={steps.tags && steps.tags.length}
|
|
||||||
onDefocus={handleDefocus('tags')}
|
|
||||||
preview={tags}
|
|
||||||
optional
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Metadata
|
|
||||||
ref={this.setIsValid('metadata')}
|
|
||||||
expanded={step === 'metadata'}
|
|
||||||
next="user-script"
|
|
||||||
saved={steps.metadata && steps.metadata.length}
|
|
||||||
onDefocus={handleDefocus('metadata')}
|
|
||||||
preview={metadata}
|
|
||||||
optional
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<UserScript
|
|
||||||
ref={this.setIsValid('user-script')}
|
|
||||||
expanded={step === 'user-script'}
|
|
||||||
next="firewall"
|
|
||||||
saved={get(steps, 'user-script.lines', false)}
|
|
||||||
onDefocus={handleDefocus('user-script')}
|
|
||||||
preview={steps['user-script']}
|
|
||||||
optional
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Firewall
|
|
||||||
ref={this.setIsValid('firewall')}
|
|
||||||
expanded={step === 'firewall'}
|
|
||||||
next="cns"
|
|
||||||
saved={steps.firewall}
|
|
||||||
onDefocus={handleDefocus('firewall')}
|
|
||||||
preview={firewall}
|
|
||||||
optional
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<CNS
|
|
||||||
ref={this.setIsValid('cns')}
|
|
||||||
expanded={step === 'cns'}
|
|
||||||
next="affinity"
|
|
||||||
saved={steps.cns}
|
|
||||||
onDefocus={handleDefocus('cns')}
|
|
||||||
preview={cns}
|
|
||||||
optional
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Affinity
|
|
||||||
ref={this.setIsValid('affinity')}
|
|
||||||
expanded={step === 'affinity'}
|
|
||||||
next=""
|
|
||||||
saved={steps.affinity}
|
|
||||||
onDefocus={handleDefocus('affinity')}
|
|
||||||
preview={affinity}
|
|
||||||
optional
|
|
||||||
/>
|
|
||||||
</Margin>
|
|
||||||
</ResourceSteps>
|
|
||||||
<Margin bottom="5">
|
|
||||||
<Flex alignCenter>
|
|
||||||
<FlexItem>
|
|
||||||
<ReduxForm form={IC_F} onSubmit={handleSubmit}>
|
|
||||||
{({ handleSubmit, submitting }) => (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<Button
|
|
||||||
disabled={disabled || !this.isFormValid()}
|
|
||||||
loading={submitting}
|
|
||||||
>
|
|
||||||
Deploy
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</ReduxForm>
|
|
||||||
</FlexItem>
|
|
||||||
<FlexItem>
|
|
||||||
<Margin left={3}>
|
|
||||||
<Anchor
|
|
||||||
disabled={disabled || !this.isFormValid()}
|
|
||||||
onClick={() => handleToggleShowCli(!showCli)}
|
|
||||||
>
|
|
||||||
View CLI Details
|
|
||||||
</Anchor>
|
|
||||||
</Margin>
|
|
||||||
</FlexItem>
|
|
||||||
</Flex>
|
|
||||||
</Margin>
|
|
||||||
</Padding>
|
|
||||||
{showCli ? (
|
|
||||||
<Fragment>
|
|
||||||
<Footer
|
|
||||||
getData={() => generatePayload(steps)}
|
|
||||||
onCloseCli={() => handleToggleShowCli(!showCli)}
|
|
||||||
/>
|
|
||||||
<Mask onClick={() => handleToggleShowCli(!showCli)} />
|
|
||||||
</Fragment>
|
|
||||||
) : null}
|
|
||||||
</ViewContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default compose(
|
|
||||||
graphql(CreateInstanceMutation, { name: 'createInstance' }),
|
|
||||||
connect(({ form, values = {} }, { match, location }) => {
|
|
||||||
const steps = {
|
|
||||||
name: values[names.name],
|
|
||||||
image: values[names.image],
|
|
||||||
package: values[names.package],
|
|
||||||
networks: values[names.networks],
|
|
||||||
tags: values[names.tags],
|
|
||||||
metadata: values[names.metadata],
|
|
||||||
'user-script': values[names['user-script']],
|
|
||||||
firewall: values[names.firewall],
|
|
||||||
cns: values[names.cns],
|
|
||||||
affinity: values[names.affinity]
|
|
||||||
};
|
|
||||||
|
|
||||||
const error = get(form, `${IC_F}.error`, null);
|
|
||||||
const showCli = Boolean(values[IC_SHOW_CLI]);
|
|
||||||
|
|
||||||
// Maybe re-use saved to only write the rule once
|
|
||||||
const disabled = !(
|
|
||||||
!error &&
|
|
||||||
steps.name &&
|
|
||||||
steps.name.name &&
|
|
||||||
steps.image &&
|
|
||||||
steps.image.id &&
|
|
||||||
steps.package &&
|
|
||||||
steps.package.id &&
|
|
||||||
steps.networks &&
|
|
||||||
Array.isArray(steps.networks)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
disabled,
|
|
||||||
showCli,
|
|
||||||
forms: Object.keys(form), // improve this
|
|
||||||
error,
|
|
||||||
steps
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
connect(null, (dispatch, { steps = {}, forms, history, createInstance }) => {
|
|
||||||
return {
|
|
||||||
handleToggleShowCli: value => {
|
|
||||||
return dispatch(set({ name: IC_SHOW_CLI, value }));
|
|
||||||
},
|
|
||||||
handleDefocus: name => value => {
|
|
||||||
return dispatch(set({ name: names[name], value }));
|
|
||||||
},
|
|
||||||
handleSubmit: async () => {
|
|
||||||
const [err, res] = await intercept(
|
|
||||||
createInstance({
|
|
||||||
variables: generatePayload(steps)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
throw new SubmissionError({
|
|
||||||
_error: parseError(err)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch([destroyAll(), forms.map(name => destroy(name))]);
|
|
||||||
history.push(`/instances/${res.data.createMachine.id}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)(CreateInstance);
|
|
Before Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 20 KiB |