Compare commits

...

57 Commits

Author SHA1 Message Date
johnytiago
078513f603 feat(instances): adds cli-details UI 2018-06-04 14:30:07 +01:00
Fábio Moreira
a1ff036db1 test(instances): updates snapshots 2018-06-04 12:48:09 +01:00
Fábio Moreira
fc81d666d0 feat(instances): CI - firewall responsive designs 2018-06-04 12:48:09 +01:00
Fábio Moreira
deb1a8436f feat(instances): user scripts responsive designs 2018-06-04 12:48:09 +01:00
Fábio Moreira
cad1431e79 feat(instances): CI - metadata resposive 2018-06-04 12:48:09 +01:00
Fábio Moreira
2f9d135319 feat(instances): CI - Responsive behavior for tags #1121 2018-06-04 12:48:09 +01:00
Fábio Moreira
c268d88a4d feat(instances): create instance - networks responsive next button 2018-06-04 12:48:09 +01:00
Fábio Moreira
6698a8eacb feat(ui-toolkit): key-value responsive 2018-06-04 12:48:09 +01:00
Fábio Moreira
1e8e89b3c8 feat(instances): theme resizer 2018-06-04 12:48:09 +01:00
Sérgio Ramos
063e40859d feat(sg): bootstrap 2018-06-04 11:46:08 +01:00
Sérgio Ramos
fc84358dff feat(templates): bootstrap 2018-06-04 11:46:08 +01:00
Fábio Moreira
6736caaf45
Implement responsive designs for CI: Networks (#1438)
* feat(instances): network section responive behaviour

* feat(instances): adjust network info container width

* test(instances): update network step snapshot

* test(instances): update network step snapshots
2018-05-28 13:38:21 +01:00
Fábio Moreira
5cb02d709c feat(instances): responsive layout for instance name card
fixes #1118
2018-05-24 10:34:55 +01:00
Fábio Moreira
b66f761a9e
#1406 - implement responsive rules on create instance (#1432)
* fix(instances): swap absolute media query value for defined breakpoint

* feat(ui-toolkit): update breakpoints

* feat(ui-toolkit): view container padding and max width

* feat(ui-toolkit): responsive image selector

* fix(ui-toolkit): delay parallax header hiding

* feat(instances): responsive rules for package selection

* test(instances): update snapshots

* fix(instances): use color variables on package card

* fix(instances): import breakpoints from ui-toolkit

* feat(instances): package selection for mobile or desktop version

* feat(instances): remove unnecessary col css props

* test(instances): update snapshots

* test(instances): update resource widgets snaphots
2018-05-23 12:41:40 +01:00
johnytiago
8422cdfe8c feat(instances): QA Instance Managment bug fixes. closes #1423 2018-05-21 11:37:26 +01:00
Joao Tiago
109988536b feat(instances): Fixes collase typo 2018-05-18 13:48:09 +01:00
johnytiago
d75ae0f14f feat(instances): Adds font antialiasing. closes #1418 2018-05-18 13:47:27 +01:00
johnytiago
5c98a4cecb feat(instances): refactor card headers #1417 2018-05-17 20:01:44 +01:00
johnytiago
0189822a08 feat(instances): bug fixes #1387 2018-05-17 18:14:00 +01:00
johnytiago
5d46689869 feat(instances): Adds better element ids 2018-05-17 18:03:08 +01:00
johnytiago
04cb9c32f8 feat(instances): Firewall empty state update, more QA 2018-05-17 17:50:11 +01:00
Fábio Moreira
8e6adb1ef4 test: update snapshots 2018-05-16 17:06:21 +01:00
Fábio Moreira
a1154b2520 feat(ui-toolkit): improve parallax scroll behaviour 2018-05-16 17:06:21 +01:00
Fábio Moreira
155a065281 feat(ui-toolkit): update spacing guidelines 2018-05-16 17:06:21 +01:00
Fábio Moreira
f388e52549 fix(ui-toolkit): add margin to playground tab headers 2018-05-16 17:06:21 +01:00
Fábio Moreira
882085a170 feat(ui-toolkit): skip parallax header after user scrolls 2018-05-16 17:06:21 +01:00
Fábio Moreira
77fd895b6c feat(ui-toolkit): add parallax header after scroll 2018-05-16 17:06:21 +01:00
Fábio Moreira
bd332423be feat(ui-toolkit): define next section for empty link 2018-05-16 17:06:21 +01:00
Fábio Moreira
91e1fb192b feat(ui-toolkit): refator bottomNav to work for subSections 2018-05-16 17:06:21 +01:00
Fábio Moreira
88fe0ea92d feat(ui-toolkit): replace bottom nav arrows with html entities 2018-05-16 17:06:21 +01:00
Fábio Moreira
8bb4c31aba feat(ui-toolkit): add bottom nav to jump to previous or next session 2018-05-16 17:06:21 +01:00
Fábio Moreira
867e9b35a0 fix(instances): change packages text and link 2018-05-11 12:58:56 +01:00
Fábio Moreira
574c3add4e fix(ui-toolkit): remove horizontal scroll on styleguide #1384 2018-05-11 12:58:27 +01:00
Fábio Moreira
c233b0d757 fix(ui-toolkit): styleguide minor bugs 2018-05-11 12:58:27 +01:00
johnytiago
84b2d67d2c feat(instances): Adds Counts, Updates Firewall 2018-05-11 12:57:39 +01:00
johnytiago
062ff0ba67 feat(instances): status icon cross, cns tags color 2018-05-11 12:57:39 +01:00
Sérgio Ramos
b00baa7028 fix(instances): don't send cns names unless they exist 2018-05-09 14:24:07 +01:00
Fábio Moreira
588b833045 fix(ui-toolkit): replace styled card with card component on section 2018-05-09 12:17:20 +01:00
Sérgio Ramos
b3edb3aa02 fix(instances): handle name/identity aff props 2018-05-09 12:06:10 +01:00
Sérgio Ramos
542b491b52 refactor(instances): s/network/networks 2018-05-09 11:36:16 +01:00
Sérgio Ramos
7d8b478d20 test(instances): update snapshots 2018-05-09 11:29:17 +01:00
Joao Tiago
7531e503f2 fix(instances): Save -> Next, Create takes to name card (#1411) 2018-05-09 11:27:33 +01:00
Sérgio Ramos
d1b2ba0002 fix(instances): add Next button to fw 2018-05-09 11:22:27 +01:00
Sérgio Ramos
98a8b2eb5b fix(instances): send tags when creating instance 2018-05-09 11:21:40 +01:00
Sérgio Ramos
de356e1fbe fix(instances): send user-script when creating instance 2018-05-09 11:11:03 +01:00
Sérgio Ramos
f9c89cbccc fix(instances): send network ids when creating instance 2018-05-09 10:57:37 +01:00
johnytiago
1317894f27 feat(instances): wip networks, tags 2018-05-09 10:52:31 +01:00
Sérgio Ramos
f007889283 fix: try to serve a static route before maping assets 2018-05-09 10:34:49 +01:00
Sérgio Ramos
831be94521 build: fix ssr build 2018-05-09 00:03:57 +01:00
Sérgio Ramos
e321c20906 fix(instances): s/userScript/user-script 2018-05-08 18:02:01 +01:00
Sérgio Ramos
429ad17262 refactor(instances): better disabled/isValid handling 2018-05-08 17:55:06 +01:00
Sérgio Ramos
d591d93547 fix: prevent image card from growing to much 2018-05-08 16:55:18 +01:00
Sérgio Ramos
33d3d4343d fix: add margins to section-list/menu 2018-05-08 16:00:11 +01:00
johnytiago
32d5cdc293 refactor(instances): refactor create instance flow
fixes #1354
2018-05-08 15:32:10 +01:00
johnytiago
316773d9b7 feat(ui-toolkit): remove all margins
fixes #1156
2018-05-08 15:32:10 +01:00
Sérgio Ramos
0ae1290a5e feat: support ssr on bundle 2018-05-07 13:34:10 +01:00
Sérgio Ramos
3f75f812fa feat: add support for react-helmet 2018-05-07 12:09:12 +01:00
1053 changed files with 86709 additions and 109998 deletions

View File

@ -1,4 +1,9 @@
packages/*/**
prototypes/*/**
artifacts
reports
.nyc_output
coverage
dist
styleguide
build
consoles/*/lib/app
node_modules

View File

@ -1,8 +1,10 @@
{
"extends": "joyent-portal",
"rules": {
"jsx-a11y/href-no-hash": 0,
"no-console": 1,
"new-cap": 0,
"no-console": 0
"jsx-a11y/href-no-hash": 0,
"no-negated-condition": 1,
"camelcase": 1
}
}

2
.gitignore vendored
View File

@ -165,3 +165,5 @@ prototypes/*/package-lock.json
_env*
keys*
/packages/*/public/index.html
/consoles/*/public/index.html

View File

@ -15,6 +15,7 @@ yarn.lock
dist
build
packages/*/lib/app
consoles/*/lib/app
*.ico
*.html

1
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -9,24 +9,23 @@
"build:lib": "echo 0",
"build:bundle": "echo 0",
"prepublish": "echo 0",
"lint": "echo 0",
"lint:ci": "echo 0",
"test": "echo 0",
"test:ci": "echo 0"
},
"dependencies": {
"apr-main": "^4.0.3",
"brule": "^3.1.0",
"cloudapi-gql": "^7.1.4",
"cloudapi-gql": "^8.0.0",
"execa": "^0.10.0",
"h2o2": "^8.0.1",
"hapi": "^17.3.1",
"hapi-triton-auth": "^2.0.1",
"hapi-webconsole-nav": "^1.2.0",
"inert": "^5.1.0",
"graphi": "^5.7.0",
"h2o2": "^8.1.2",
"hapi": "^17.5.0",
"hapi-triton-auth": "^3.0.0",
"hapi-webconsole-nav": "^2.1.0",
"my-joy-images": "*",
"my-joy-instances": "*",
"my-joy-navigation": "*",
"rollover": "^1.0.0"
"my-joy-service-groups": "*",
"my-joy-templates": "*",
"tsg-graphql": "^1.0.0"
}
}

View File

@ -1,7 +1,12 @@
require('../.env.js');
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
const Ui = require('my-joy-images');
const {
PORT = 4003,
@ -25,7 +30,19 @@ Main(async () => {
BASE_URL
});
await server.register({
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: CloudApiGql,
options: {
authStrategy: 'sso',
@ -37,7 +54,11 @@ Main(async () => {
routes: {
prefix: `/${PREFIX}`
}
});
},
{
plugin: Ui
}
]);
await server.start();
});

View File

@ -1,7 +1,12 @@
require('../.env.js');
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
const Ui = require('my-joy-instances');
const {
PORT = 4002,
@ -25,7 +30,19 @@ Main(async () => {
BASE_URL
});
await server.register({
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: CloudApiGql,
options: {
authStrategy: 'sso',
@ -37,7 +54,11 @@ Main(async () => {
routes: {
prefix: `/${PREFIX}`
}
});
},
{
plugin: Ui
}
]);
await server.start();
});

View File

@ -1,7 +1,12 @@
require('../.env.js');
const Main = require('apr-main');
const Nav = require('hapi-webconsole-nav');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
const Ui = require('my-joy-navigation');
const Regions = require('../data/regions');
const Categories = require('../data/categories');
@ -30,7 +35,19 @@ Main(async () => {
BASE_URL
});
await server.register({
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: Nav,
options: {
keyPath,
@ -45,7 +62,11 @@ Main(async () => {
routes: {
prefix: `/${PREFIX}`
}
});
},
{
plugin: Ui
}
]);
await server.start();
});

View File

@ -1,6 +1,7 @@
require('../.env.js');
const Hapi = require('hapi');
const Sso = require('hapi-triton-auth');
const Url = require('url');
const {
COOKIE_PASSWORD,
@ -8,12 +9,10 @@ const {
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID,
SDC_URL,
DC_NAME
SDC_URL
} = process.env;
module.exports = async ({ PORT, BASE_URL }) => {
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
const apiBaseUrl = SDC_URL;
@ -50,6 +49,7 @@ module.exports = async ({ PORT, BASE_URL }) => {
server.events.on('log', (event, tags) => {
if (tags.error) {
// eslint-disable-next-line no-console
console.log(event);
}
});
@ -58,6 +58,7 @@ module.exports = async ({ PORT, BASE_URL }) => {
const { tags } = event;
if (tags.includes('error') && event.data && event.data.errors) {
event.data.errors.forEach(error => {
// eslint-disable-next-line no-console
console.log(error);
});
}

View File

@ -0,0 +1,78 @@
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();
});

78
bundle/src/templates.js Normal file
View File

@ -0,0 +1,78 @@
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();
});

View File

@ -4,7 +4,16 @@ module.exports = {
'scope-enum': [
2,
'always',
['ui-toolkit', 'icons', 'instances', 'navigation', 'bundle', 'images']
[
'ui-toolkit',
'icons',
'instances',
'navigation',
'bundle',
'images',
'sg',
'templates'
]
]
}
};

View File

@ -1,16 +1,29 @@
const Boom = require('boom');
const Inert = require('inert');
const Path = require('path');
const RenderReact = require('hapi-render-react');
const Wreck = require('wreck');
const Url = require('url');
const Intercept = require('apr-intercept');
const Fs = require('mz/fs');
const { NAMESPACE = 'instances' } = process.env;
const { NAMESPACE = 'images', 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/${NAMESPACE}/static/`);
const buildRoot = Path.join(__dirname, '../build');
const buildStatic = Path.join(buildRoot, `${NAMESPACE}`);
const publicRoot = Path.join(__dirname, `../public/static/`);
await server.register([
@ -57,15 +70,41 @@ exports.register = async server => {
const { params } = request;
const { rest } = params;
const publicPathname = Path.join(publicRoot, rest);
const buildPathname = Path.join(buildRoot, rest);
if (!rest) {
return Boom.notFound();
}
const [err] = await Intercept(
const publicPathname = Path.join(publicRoot, rest);
const [err1] = await Intercept(
Fs.access(publicPathname, Fs.constants.R_OK)
);
const file = err ? buildPathname : publicPathname;
return h.file(file, { confine: false });
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
});
}
},
{

View File

@ -6,13 +6,11 @@
"repository": "github:yldio/joyent-portal",
"main": "lib/index.js",
"scripts": {
"dev": "NAMESPACE=images NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3070 joyent-react-scripts start",
"dev": "REACT_APP_DEV=1 NAMESPACE=images NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3070 joyent-react-scripts start",
"build:test": "echo 0",
"build:lib": "echo 0",
"build:bundle": "NAMESPACE=images NODE_ENV=production redrun -p build:frontend build:ssr",
"prepublish": "NODE_ENV=production redrun build:bundle",
"lint": "redrun lint:ci -- --fix",
"lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md",
"test": "echo 0",
"test:ci": "echo 0",
"build:frontend": "joyent-react-scripts build",
@ -21,22 +19,24 @@
"dependencies": {
"@manaflair/redux-batch": "^0.1.0",
"apollo": "^0.2.2",
"apollo-cache-inmemory": "^1.1.12",
"apollo-client": "^2.2.8",
"apollo-link-http": "^1.5.3",
"apollo-cache-inmemory": "^1.2.2",
"apollo-client": "^2.3.2",
"apollo-link-http": "^1.5.4",
"apr-intercept": "^3.0.3",
"apr-reduce": "^3.0.3",
"cross-fetch": "^2.1.0",
"boom": "^7.2.0",
"cross-fetch": "^2.2.0",
"date-fns": "^1.29.0",
"declarative-redux-form": "^2.0.8",
"exenv": "^1.2.2",
"force-array": "^3.1.0",
"fuse.js": "^3.2.0",
"hapi-render-react": "^2.5.2",
"hapi-render-react-joyent-document": "^5.0.0",
"hapi-render-react-joyent-document": "^7.2.0",
"inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0",
"joyent-react-styled-flexboxgrid": "^2.2.3",
"joyent-react-styled-flexboxgrid": "^3.1.0",
"joyent-ui-resource-widgets": "^1.0.0",
"joyent-ui-toolkit": "^6.0.0",
"lodash.assign": "^4.2.0",
"lodash.find": "^4.6.0",
@ -46,36 +46,37 @@
"lodash.keys": "^4.2.0",
"lodash.omit": "^4.5.0",
"lodash.uniqby": "^4.7.0",
"lunr": "^2.1.6",
"lunr": "^2.2.1",
"mz": "^2.7.0",
"param-case": "^2.1.1",
"react": "^16.3.1",
"react-apollo": "^2.1.2",
"react-dom": "^16.3.1",
"react": "^16.4.0",
"react-apollo": "^2.1.4",
"react-dom": "^16.4.0",
"react-helmet-async": "0.1.0",
"react-redux": "^5.0.7",
"react-redux-values": "^1.1.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2",
"redux": "^4.0.0",
"redux-form": "^7.3.0",
"remcalc": "^1.0.10",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-components": "^3.3.0",
"styled-components-spacing": "^3.0.0",
"styled-flex-component": "^2.2.2",
"styled-is": "^1.1.2",
"styled-is": "^1.1.3",
"title-case": "^2.1.1",
"yup": "^0.24.1"
"yup": "^0.25.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-image-snapshot": "^2.4.0",
"jest-image-snapshot": "^2.4.2",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.0.3",
"joyent-react-scripts": "^8.2.1",
"react-screenshot-renderer": "^1.1.2",
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
"react-test-renderer": "^16.4.0",
"redrun": "^6.0.4"
}
}

View File

@ -51,6 +51,7 @@ const getState = request => {
module.exports = Document({
namespace: 'images/',
assets,
Html: require('./html'),
indexFile,
getState
});

View File

@ -1,10 +1,14 @@
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>Images</title>
</Helmet>
<Routes />
</RootContainer>
);

View File

@ -2,11 +2,9 @@ import React from 'react';
import { Field } from 'redux-form';
import { Margin } from 'styled-components-spacing';
import Flex, { FlexItem } from 'styled-flex-component';
import remcalc from 'remcalc';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import {
Divider,
FormGroup,
FormLabel,
Input,
@ -22,18 +20,16 @@ export default ({ placeholderName, randomizing, onRandomize }) => (
<FlexItem flex>
<FormGroup name="name" fluid field={Field}>
<FormLabel>Image name</FormLabel>
<Margin top={0.5}>
<Margin top="0.5">
<Input placeholder={placeholderName} onBlur={null} required />
</Margin>
<FormMeta />
</FormGroup>
</FlexItem>
<FlexItem>
<Divider height={remcalc(13)} transparent />
<Margin left={1}>
<Margin left="1">
<Button
type="button"
marginTop={remcalc(8)}
onClick={onRandomize}
loading={randomizing}
marginless
@ -46,21 +42,21 @@ export default ({ placeholderName, randomizing, onRandomize }) => (
</Margin>
</FlexItem>
</Flex>
<Margin top={3}>
<Margin top="3">
<FormGroup name="version" fluid field={Field}>
<FormLabel>Version</FormLabel>
<Margin top={0.5}>
<Margin top="0.5">
<Input placeholder="Example: v1.0" onBlur={null} required />
</Margin>
<FormMeta />
</FormGroup>
</Margin>
<Row>
<Col xs={12} sm={8}>
<Margin top={3}>
<Col xs="12" sm="8">
<Margin top="3">
<FormGroup name="description" fluid field={Field}>
<FormLabel>Description</FormLabel>
<Margin top={0.5}>
<Margin top="0.5">
<Textarea
placeholder="Example: JarJarBinks, Anakin Skywalker, Obi Wan Kenobi, Qui-Gon Jinn, Han Solo, Wookies"
fluid

View File

@ -16,14 +16,14 @@ const Container = styled.div`
export default ({ icon, children, collapsed = true, ...rest }) => (
<Container {...rest}>
<Flex>
<Margin right={1}>
<Margin right="1">
<Flex alignCenter full>
{icon}
</Flex>
</Margin>
<Small noMargin>{children}</Small>
</Flex>
<Margin top={1} bottom={collapsed ? 7 : 3}>
<Margin top="1" bottom={collapsed ? 7 : 3}>
<Divider height={remcalc(1)} />
</Margin>
</Container>

View File

@ -5,8 +5,8 @@ import { P } from 'joyent-ui-toolkit';
export default ({ children }) => (
<Row>
<Col xs={12} sm={8}>
<Margin bottom={3}>
<Col xs="12" sm="8">
<Margin bottom="3">
<P>{children}</P>
</Margin>
</Col>

View File

@ -4,7 +4,7 @@ import { Margin, Padding } from 'styled-components-spacing';
import Flex from 'styled-flex-component';
import { H3, Card } from 'joyent-ui-toolkit';
import { NoPackages } from 'joyent-logo-assets';
import { EmptyState } from 'joyent-icons';
const NoPackagesTitle = styled(H3)`
color: ${props => props.theme.greyDark};
@ -17,10 +17,10 @@ const FullWidthCard = styled(Card)`
export default ({ children }) => (
<FullWidthCard>
<Padding all={6}>
<Padding all="6">
<Flex alignCenter justifyCenter column>
<Margin bottom={2}>
<NoPackages />
<Margin bottom="2">
<EmptyState />
</Margin>
<NoPackagesTitle>{children}</NoPackagesTitle>
</Flex>

View File

@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { Field } from 'redux-form';
import Flex from 'styled-flex-component';
import Flex, { FlexItem } from 'styled-flex-component';
import { Padding, Margin } from 'styled-components-spacing';
import remcalc from 'remcalc';
@ -15,7 +15,7 @@ import {
PopoverTarget,
Popover,
PopoverItem,
PopoverDivider,
PopoverDivider as BasePopoverDivider,
PopoverContainer,
Radio,
FormLabel,
@ -56,11 +56,6 @@ const Content = styled(Padding)`
const Max = styled(Flex)`
max-width: 100%;
height: 100%;
`;
const DividerContainer = styled(Margin)`
height: 100%;
`;
const Actions = styled(Flex)`
@ -71,9 +66,14 @@ const Actions = styled(Flex)`
const ActionsWrapper = styled(Flex)`
height: 100%;
width: 100%;
border-left: ${remcalc(1)} solid ${props => props.theme.grey};
`;
const PopoverDivider = styled(BasePopoverDivider)`
width: 100%;
`;
export const Image = ({
name,
id,
@ -84,39 +84,41 @@ export const Image = ({
onRemove,
onCreateInstance
}) => (
<Margin bottom={3}>
<Margin bottom="3">
<CardAnchor to={`/images/${id}`} component={Link}>
<Card radius>
{removing ? (
<Padding all={2}>
<Padding all="2">
<StatusLoader />
</Padding>
) : (
<Fragment>
<CardHeader white radius>
<Padding left={2} right={2}>
<Padding left="2" right="2">
<Flex full alignCenter>
<Margin right={2}>
<FlexItem>
<Margin right="2">
{React.createElement(OS[os], {
width: '24',
height: '24'
})}
</Margin>
</FlexItem>
<FlexItem>
<A to={`/images/${id}/summary`} component={Link}>
{name}
</A>
</FlexItem>
</Flex>
</Padding>
</CardHeader>
<Flex justifyBetween>
<Content left={2} top={2} bottom={2}>
<Content left="2" top="2" bottom="2">
<Max justifyBetween>
<Max alignCenter>
<Flex>{version}</Flex>
<DividerContainer left={2}>
<Divider width={remcalc(1)} height="100%" />
</DividerContainer>
<Type left={2}>{ImageType[type]}</Type>
<Divider vertical />
<Type>{ImageType[type]}</Type>
</Max>
</Max>
</Content>
@ -127,7 +129,8 @@ export const Image = ({
<ActionsIcon />
</ActionsWrapper>
</PopoverTarget>
<Popover placement="bottom">
<Popover noPadding placement="bottom">
<Padding horizontal="3" vertical="2">
<PopoverItem disabled={false} onClick={onCreateInstance}>
<ItemAnchor
href={`${
@ -139,10 +142,13 @@ export const Image = ({
Create Instance
</ItemAnchor>
</PopoverItem>
</Padding>
<PopoverDivider />
<Padding horizontal="3" vertical="2">
<PopoverItem disabled={removing} onClick={onRemove}>
Remove
</PopoverItem>
</Padding>
</Popover>
</Actions>
</PopoverContainer>
@ -157,9 +163,9 @@ export const Image = ({
export const Filters = ({ selected }) => (
<Fragment>
<FormGroup name="image-type" value="all" field={Field} type="radio">
<Radio noMargin>
<Radio>
<Flex alignCenter>
<Margin right={2}>
<Margin horizontal="2">
<FormLabel big normal={selected !== 'all'}>
All
</FormLabel>
@ -175,7 +181,7 @@ export const Filters = ({ selected }) => (
>
<Radio noMargin>
<Flex alignCenter>
<Margin right={2}>
<Margin horizontal="2">
<FormLabel big normal={selected !== 'hardware-virtual-machine'}>
Virtual machines
</FormLabel>
@ -191,7 +197,7 @@ export const Filters = ({ selected }) => (
>
<Radio noMargin>
<Flex alignCenter>
<Margin right={2}>
<Margin horizontal="2">
<FormLabel big normal={selected !== 'infrastructure-container'}>
Infrastructure container
</FormLabel>

View File

@ -1,7 +1,7 @@
import React from 'react';
import forceArray from 'force-array';
import { Margin } from 'styled-components-spacing';
import { NavLink } from 'react-router-dom';
import forceArray from 'force-array';
import {
SectionList,
@ -28,7 +28,7 @@ const Menu = ({ links = [] }) => {
return (
<ViewContainer plain>
<Margin bottom={6}>
<Margin bottom="5" top="1">
<SectionList>{getMenuItems(_links)}</SectionList>
</Margin>
</ViewContainer>

View File

@ -1,18 +1,19 @@
import React, { Fragment } from 'react';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import { Margin, Padding } from 'styled-components-spacing';
import styled, { withTheme } from 'styled-components';
import styled from 'styled-components';
import Flex, { FlexItem } from 'styled-flex-component';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import titleCase from 'title-case';
import remcalc from 'remcalc';
import { ValueBreakpoints as breakpoints } from 'joyent-ui-toolkit';
import {
Card,
CardOutlet,
H2,
P,
Label,
Label as BaseLabel,
Divider,
Button,
QueryBreakpoints,
@ -37,11 +38,15 @@ const VerticalDivider = styled.div`
align-self: flex-end;
margin: 0 ${remcalc(18)};
@media (max-width: ${remcalc(767)}) {
@media (max-width: ${remcalc(breakpoints.small.upper)}) {
display: none;
}
`;
const Label = styled(BaseLabel)`
font-weight: 200;
`;
const GreyLabel = styled(Label)`
opacity: 0.5;
padding-right: ${remcalc(3)};
@ -71,7 +76,7 @@ export const Meta = ({ name, version, type, published_at, state, os }) => (
<H2 bold>{name}</H2>
</FlexItem>
</Flex>
<Margin top={2} bottom={3}>
<Margin top="2" bottom="3">
<Flex>
<Label>{version}</Label>
<VerticalDivider />
@ -99,15 +104,15 @@ export const Meta = ({ name, version, type, published_at, state, os }) => (
</Fragment>
);
export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
export default ({ theme = {}, onRemove, removing, ...image }) => (
<Row>
<Col xs={12} sm={12} md={9}>
<Col xs="12" sm="12" md="9">
<Card>
<CardOutlet>
<Padding all={4}>
<Padding all="5">
<Meta {...image} />
<Row between="xs">
<Col xs={9}>
<Col xs="9">
<SmallOnly>
<Button type="button" small icon>
<DuplicateIcon light />
@ -124,15 +129,14 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
bold
icon
>
<DuplicateIcon light />
<span>Create Instance</span>
</Button>
</Medium>
</Col>
<Col xs={3}>
<Col xs="3">
<SmallOnly>
<Button type="button" small icon error right>
<DeleteIcon fill={theme.red} />
<DeleteIcon fill="red" />
</Button>
</SmallOnly>
<Medium>
@ -145,8 +149,10 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
error
right
>
<DeleteIcon fill={theme.red} />
<span>Remove</span>
<Margin right="1">
<DeleteIcon fill="red" />
</Margin>
<span>Delete</span>
</Button>
</Medium>
</Col>
@ -157,10 +163,15 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
<Margin bottom="2">
<P>{image.description}</P>
</Margin>
<Margin bottom="3">
<CopiableField text={(image.id || '').split('-')[0]} label="ID" />
</Margin>
<Margin bottom="3">
<CopiableField text={image.id} label="UUID" />
</Margin>
<Row>
<Col xs={12} md={7}>
<Col xs="12" md="7">
<Margin bottom="3">
<FormLabel>Operating system</FormLabel>
<Input
monospace
@ -168,6 +179,7 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
fluid
value={titleCase(image.os)}
/>
</Margin>
</Col>
</Row>
</Padding>
@ -175,4 +187,4 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
</Card>
</Col>
</Row>
));
);

View File

@ -1,7 +1,8 @@
import React from 'react';
import { Margin } from 'styled-components-spacing';
import { TagItem, KeyValue } from 'joyent-ui-toolkit';
import { TagItem } from 'joyent-ui-toolkit';
import { KeyValue } from 'joyent-ui-resource-widgets';
export const AddForm = props => (
<KeyValue {...props} method="add" input="input" type="tag" expanded />
@ -13,8 +14,8 @@ export const EditForm = props => (
export default ({ norMargin, name, value, onClick, onRemoveClick, active }) => (
<Margin
right={norMargin ? 0 : 1}
bottom={norMargin ? 0 : 1}
right={norMargin ? '0' : '1'}
bottom={norMargin ? '0' : '1'}
key={`${name}-${value}`}
>
<TagItem onClick={onClick} active={active} onRemoveClick={onRemoveClick}>

View File

@ -2,15 +2,8 @@ import React from 'react';
import { Field } from 'redux-form';
import Flex from 'styled-flex-component';
import { Margin } from 'styled-components-spacing';
import remcalc from 'remcalc';
import {
Button,
FormGroup,
Input,
FormLabel,
Divider
} from 'joyent-ui-toolkit';
import { Button, FormGroup, Input, FormLabel } from 'joyent-ui-toolkit';
export const Toolbar = ({
searchable = true,
@ -21,16 +14,15 @@ export const Toolbar = ({
actionable = false,
onActionClick
}) => (
<Flex justifyBetween>
<Flex justifyBetween alignEnd>
<FormGroup name="filter" field={Field}>
<FormLabel>{searchLabel}</FormLabel>
<Margin top={0.5}>
<Margin top="0.5">
<Input placeholder={searchPlaceholder} disabled={!searchable} />
</Margin>
</FormGroup>
{action ? (
<FormGroup right>
<Divider height={remcalc(21)} transparent />
<Button
type="button"
disabled={!actionable}
@ -48,6 +40,5 @@ export const Toolbar = ({
export default ({ handleSubmit, ...rest }) => (
<form onSubmit={handleSubmit}>
<Toolbar {...rest} />
<Divider height={remcalc(20)} transparent />
</form>
);

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Margin } from 'styled-components-spacing';
import paramCase from 'param-case';
import get from 'lodash.get';
@ -43,7 +44,9 @@ export default ({ match }) => {
.filter(Boolean)
.map(({ name, pathname }) => (
<BreadcrumbItem key={name} to={pathname} component={Link}>
<Margin horizontal="1" vertical="3">
{name}
</Margin>
</BreadcrumbItem>
));

View File

@ -67,12 +67,12 @@ const NameContainer = ({
onRandomize={handleRandomize}
/>
) : name ? (
<Margin top={3}>
<Margin top="3">
<H3 bold noMargin>
{name}
</H3>
{version ? (
<Margin top={2}>
<Margin top="2">
<H4 bold noMargin>
{version}
</H4>
@ -80,8 +80,8 @@ const NameContainer = ({
) : null}
{description ? (
<Row>
<Col xs={12} sm={8}>
<Margin top={1}>
<Col xs="12" sm="8">
<Margin top="1">
<P>{description}</P>
</Margin>
</Col>
@ -92,13 +92,13 @@ const NameContainer = ({
}
</ReduxForm>
{expanded ? (
<Margin top={4} bottom={7}>
<Margin top="4" bottom="7">
<Button type="button" disabled={!name} onClick={handleNext}>
Next
</Button>
</Margin>
) : proceeded ? (
<Margin top={4} bottom={7}>
<Margin top="4" bottom="7">
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>
@ -166,6 +166,7 @@ export default compose(
dispatch(set({ name: 'create-image-name-randomizing', value: false }));
if (err) {
// eslint-disable-next-line no-console
console.error(err);
return;
}

View File

@ -6,17 +6,10 @@ import { destroy, reset } from 'redux-form';
import ReduxForm from 'declarative-redux-form';
import { connect } from 'react-redux';
import get from 'lodash.get';
import remcalc from 'remcalc';
import Flex from 'styled-flex-component';
import {
TagsIcon,
Button,
H3,
TagList,
Divider,
KeyValue
} from 'joyent-ui-toolkit';
import { TagsIcon, Button, H3, TagList } from 'joyent-ui-toolkit';
import { KeyValue } from 'joyent-ui-resource-widgets';
import Title from '@components/create-image/title';
import Description from '@components/description';
@ -66,7 +59,7 @@ export const Tags = ({
) : null}
{proceeded || expanded ? (
<Fragment>
<Margin bottom={4}>
<Margin bottom="5">
<H3>
{tags.length} Tag{tags.length === 1 ? '' : 's'}
</H3>
@ -102,12 +95,11 @@ export const Tags = ({
expanded
onCancel={() => handleChangeAddOpen(false)}
/>
<Divider height={remcalc(18)} transparent />
</Fragment>
) : null
}
</ReduxForm>
<Margin top={1}>
<Margin top="1">
<Flex alignCenter>
{expanded ? (
<Button
@ -118,11 +110,11 @@ export const Tags = ({
Add Tag
</Button>
) : null}
<Margin left={1}>{children}</Margin>
<Margin left="1">{children}</Margin>
</Flex>
</Margin>
{proceeded ? (
<Margin top={1}>
<Margin top="1">
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>

View File

@ -1,5 +1,3 @@
/* eslint-disable camelcase */
import React from 'react';
import { Margin } from 'styled-components-spacing';
import ReduxForm from 'declarative-redux-form';
@ -41,12 +39,12 @@ const Create = ({
}) => (
<ViewContainer>
{loading ? (
<Margin top={4}>
<Margin top="4">
<StatusLoader />
</Margin>
) : null}
{loadingError ? (
<Margin top={4}>
<Margin top="4">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{loadingError}</MessageDescription>
@ -54,7 +52,7 @@ const Create = ({
</Margin>
) : null}
{!loading && !loadingError ? (
<Margin top={4} bottom={4}>
<Margin top="4" bottom="5">
<H2>Create Image</H2>
</Margin>
) : null}
@ -78,7 +76,7 @@ const Create = ({
{({ handleSubmit, submitting }) =>
!loading && !loadingError ? (
<form onSubmit={handleSubmit}>
<Margin top={step === 'tag' ? 7 : 4}>
<Margin top={step === 'tag' ? '7' : '4'}>
<Button disabled={disabled} loading={submitting}>
Create Image
</Button>

View File

@ -2,7 +2,6 @@ import React, { Fragment } from 'react';
import { compose, graphql } from 'react-apollo';
import ReduxForm from 'declarative-redux-form';
import { Margin } from 'styled-components-spacing';
import remcalc from 'remcalc';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import { connect } from 'react-redux';
import get from 'lodash.get';
@ -41,20 +40,21 @@ export const List = ({
handleRemove
}) => (
<ViewContainer main>
<Divider height={remcalc(30)} transparent />
<Margin top="4">
<ReduxForm form={LIST_TOOLBAR_FORM}>
{props => <ToolbarForm {...props} actionable={!loading} />}
</ReduxForm>
<Divider height={remcalc(1)} />
<Divider height={remcalc(24)} transparent />
</Margin>
<Margin vertical="4">
<Divider />
</Margin>
{loading && !images.length ? (
<Fragment>
<Divider height={remcalc(30)} transparent />
<StatusLoader />
</Fragment>
) : null}
{error && !images.length && !loading ? (
<Margin bottom={4}>
<Margin bottom="5">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -64,7 +64,7 @@ export const List = ({
</Margin>
) : null}
<Fragment>
<Margin bottom={4}>
<Margin bottom="4">
<ReduxForm
form={LIST_TOGGLE_TYPE_FORM}
initialValues={{ 'image-type': 'all' }}
@ -78,7 +78,7 @@ export const List = ({
</Margin>
<Row>
{images.map(image => (
<Col sm={4}>
<Col sm="4">
<Image
{...image}
onCreateInstance={() => handleCreateInstance(image)}

View File

@ -10,7 +10,7 @@ const SECTIONS = [
export default ({ match }) => {
const imageId = get(match, 'params.image');
const sections = imageId !== '~create' ? SECTIONS : [];
const sections = imageId === '~create' ? [] : SECTIONS;
const links = sections.map(({ name, pathname }) => ({
name,

View File

@ -1,16 +1,14 @@
import React, { Fragment } from 'react';
import React from 'react';
import { compose, graphql } from 'react-apollo';
import { Margin } from 'styled-components-spacing';
import { connect } from 'react-redux';
import intercept from 'apr-intercept';
import { set } from 'react-redux-values';
import get from 'lodash.get';
import remcalc from 'remcalc';
import {
ViewContainer,
StatusLoader,
Divider,
Message,
MessageTitle,
MessageDescription
@ -30,14 +28,9 @@ export const Summary = ({
handleRemove
}) => (
<ViewContainer main>
{loading && !image ? (
<Fragment>
<Divider height={remcalc(30)} transparent />
<StatusLoader />
</Fragment>
) : null}
{loading && !image ? <StatusLoader /> : null}
{error && !loading && !image ? (
<Margin bottom={4}>
<Margin bottom="5">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -47,7 +40,7 @@ export const Summary = ({
</Margin>
) : null}
{mutationError ? (
<Margin bottom={4}>
<Margin bottom="5">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -14,11 +14,11 @@ import {
H3,
ViewContainer,
StatusLoader,
Divider,
Message,
MessageTitle,
MessageDescription,
TagList
TagList,
Divider
} from 'joyent-ui-toolkit';
import { Forms } from '@root/constants';
@ -56,12 +56,14 @@ export const Tags = ({
onActionClick={handleToggleAddOpen}
action
/>
<Margin vertical="4">
<Divider height={remcalc(1)} />
</Margin>
</Margin>
)}
</ReduxForm>
{error && !loading && !tags.length ? (
<Margin bottom={4}>
<Margin bottom="5">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -71,7 +73,7 @@ export const Tags = ({
</Margin>
) : null}
{mutationError ? (
<Margin bottom={4}>
<Margin bottom="5">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription>
@ -80,13 +82,13 @@ export const Tags = ({
) : null}
<ReduxForm
form={TAGS_ADD_FORM}
asyncValidate={handleAsyncValidate}
shouldAsyncValidate={shouldAsyncValidate}
asyncValidate={handleAsyncValidate}
onSubmit={handleAddTag}
>
{props =>
addOpen ? (
<Margin bottom={4}>
<Margin bottom="5">
<AddForm
{...props}
onToggleExpanded={() => handleToggleAddOpen(!addOpen)}
@ -96,18 +98,19 @@ export const Tags = ({
) : null
}
</ReduxForm>
{!loading ? (
<Margin bottom={4}>
{loading ? null : (
<Margin bottom="5">
<H3>
{tags.length} tag{tags.length === 1 ? '' : 's'}
</H3>
</Margin>
) : null}
)}
{loading && !tags.length ? <StatusLoader /> : null}
<TagList>
{tags.map(({ name, value }) => (
{tags.map(({ id, name, value }) => (
<Tag
key={value}
key={id}
id={id}
name={name}
value={value}
onRemoveClick={!mutating && (() => handleRemoveTag(name))}

View File

@ -0,0 +1,28 @@
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>
);

View File

@ -1,5 +1,6 @@
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';
@ -21,7 +22,9 @@ ReactDOM.hydrate(
<ThemeProvider theme={theme}>
<ReduxProvider store={createStore()}>
<BrowserRouter>
<HelmetProvider context={{}}>
<App />
</HelmetProvider>
</BrowserRouter>
</ReduxProvider>
</ThemeProvider>

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { Fragment } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import get from 'lodash.get';
@ -19,6 +19,8 @@ import Create from '@containers/create';
import Tags from '@containers/tags';
import { Route as ServerError } from '@root/server-error';
const { REACT_APP_DEV = false } = process.env;
export default () => (
<PageContainer>
{/* Breadcrumb */}
@ -70,6 +72,41 @@ export default () => (
<Route path="/" exact component={() => <Redirect to="/images" />} />
{REACT_APP_DEV ? (
<Fragment>
<Route
path="/instances"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3069${
location.pathname
}${location.search}`
)
}
/>
<Route
path="/templates"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3071${
location.pathname
}${location.search}`
)
}
/>
<Route
path="/service-groups"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3072${
location.pathname
}${location.search}`
)
}
/>
</Fragment>
) : null}
<noscript>
<ViewContainer main>
<Message warning>

View File

@ -17,7 +17,7 @@ import Breadcrumb from '@containers/breadcrumb';
export const Route = () => (
<ViewContainer main>
<Divider height={remcalc(30)} transparent />
<Margin bottom={4}>
<Margin bottom="5">
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -0,0 +1,42 @@
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
});
};

View File

@ -1,8 +1,12 @@
import { canUseDOM } from 'exenv';
import queryString from 'query-string';
export default (() => {
const { NODE_ENV = 'development' } = process.env;
export const Global = () => {
if (!canUseDOM) {
return {
protocol: NODE_ENV === 'development' ? 'http:' : 'https:',
cookie: ''
};
}
@ -11,10 +15,15 @@ export default (() => {
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();

View File

@ -2,7 +2,7 @@ import intercept from 'apr-intercept';
import keys from 'lodash.keys';
import reduce from 'apr-reduce';
import assign from 'lodash.assign';
import yup from 'yup';
import * as yup from 'yup';
/*****************************************************************************/
@ -14,14 +14,16 @@ const validateField = async (field, value) => {
const validateSchema = async (schema, value) => {
const errors = await reduce(
keys(schema),
async (errors, name) =>
assign(errors, {
[name]: await validateField(schema[name], value[name])
}),
async (errors, name) => {
const msg = await validateField(schema[name], value[name]);
return msg ? assign(errors, { [name]: msg }) : errors;
},
{}
);
if (keys(errors).length) {
throw errors;
}
};
/*****************************************************************************/
@ -64,4 +66,4 @@ const Schemas = {
export const addTag = tag => validateSchema(Schemas.tag, tag);
export const instanceName = ({ name }) =>
!name ? null : validateSchema(Schemas.instanceName, { name });
name ? validateSchema(Schemas.instanceName, { name }) : null;

View File

@ -0,0 +1,133 @@
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');

View File

@ -6,39 +6,38 @@
"repository": "github:yldio/joyent-portal",
"main": "lib/index.js",
"scripts": {
"dev": "NAMESPACE=instances NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3069 joyent-react-scripts start",
"dev": "REACT_APP_DEV=1 NAMESPACE=instances NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3069 joyent-react-scripts start",
"build:test": "echo 0",
"build:lib": "echo 0",
"build:bundle": "NAMESPACE=instances NODE_ENV=production redrun -p build:frontend build:ssr",
"prepublish": "NODE_ENV=production redrun build:bundle",
"lint": "redrun lint:ci -- --fix",
"lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md",
"test": "DEFAULT_TIMEOUT_INTERVAL=100000 NODE_ENV=test joyent-react-scripts test --env=jsdom",
"test": "DEFAULT_TIMEOUT_INTERVAL=100000 NODE_ENV=test joyent-react-scripts test --env=jsdom --testPathIgnorePatterns='.ui.js'",
"test:ci": "NODE_ENV=test joyent-react-scripts test --env=jsdom --testPathIgnorePatterns='.ui.js'",
"build:frontend": "joyent-react-scripts build",
"build:ssr": "SSR=1 UMD=1 babel src --out-dir lib/app --copy-files"
},
"dependencies": {
"@manaflair/redux-batch": "^0.1.0",
"apollo-cache-inmemory": "^1.1.12",
"apollo-client": "^2.2.8",
"apollo-link-http": "^1.5.3",
"apollo-cache-inmemory": "^1.2.2",
"apollo-client": "^2.3.2",
"apollo-link-http": "^1.5.4",
"apr-intercept": "^3.0.3",
"apr-reduce": "^3.0.3",
"boom": "^7.2.0",
"bytes": "^3.0.0",
"clipboard-copy": "^2.0.0",
"constant-case": "^2.0.0",
"cross-fetch": "^2.1.0",
"cross-fetch": "^2.2.0",
"date-fns": "^1.29.0",
"declarative-redux-form": "^2.0.8",
"exenv": "^1.2.2",
"fuse.js": "^3.2.0",
"hapi-render-react": "^2.5.2",
"hapi-render-react-joyent-document": "^5.0.0",
"hapi-render-react-joyent-document": "^7.2.0",
"inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0",
"joyent-ui-resource-step": "^1.0.0",
"joyent-manifest-editor": "^1.4.0",
"joyent-react-styled-flexboxgrid": "^2.2.3",
"joyent-react-styled-flexboxgrid": "^3.1.0",
"joyent-ui-toolkit": "^6.0.0",
"lodash.find": "^4.6.0",
"lodash.findindex": "^4.6.0",
@ -52,41 +51,41 @@
"lodash.isfunction": "^3.0.9",
"lodash.isinteger": "^4.0.4",
"lodash.isnan": "^3.0.2",
"lodash.omit": "^4.5.0",
"lodash.reduce": "^4.6.0",
"lodash.reverse": "^4.0.1",
"lodash.some": "^4.6.0",
"lodash.sortby": "^4.7.0",
"lodash.uniqby": "^4.7.0",
"lodash.values": "^4.3.0",
"mz": "^2.7.0",
"param-case": "^2.1.1",
"query-string": "^6.0.0",
"react": "^16.3.1",
"react-apollo": "^2.1.2",
"react-dom": "^16.3.1",
"query-string": "^6.1.0",
"react": "^16.4.0",
"react-apollo": "^2.1.4",
"react-dom": "^16.4.0",
"react-helmet-async": "0.1.0",
"react-redux": "^5.0.7",
"react-redux-values": "^1.1.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2",
"redux": "^4.0.0",
"redux-form": "^7.3.0",
"remcalc": "^1.0.10",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-components": "^3.3.0",
"styled-components-spacing": "^3.0.0",
"styled-flex-component": "^2.2.2",
"title-case": "^2.1.1",
"yup": "^0.24.1"
"yup": "^0.25.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-image-snapshot": "^2.4.0",
"jest-image-snapshot": "^2.4.2",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.0.3",
"joyent-react-scripts": "^8.2.1",
"react-screenshot-renderer": "^1.1.2",
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
"react-test-renderer": "^16.4.0",
"redrun": "^6.0.4"
}
}

Some files were not shown because too many files have changed in this diff Show More