feat(my-joy-beta): imc networks

fixes #1074
This commit is contained in:
Sérgio Ramos 2018-01-23 12:20:36 +00:00 committed by Sérgio Ramos
parent bbfa427a92
commit 8d7455c07c
44 changed files with 4089 additions and 1005 deletions

View File

@ -14,7 +14,13 @@ export default ({
}) => (
<Rotate direction={direction}>
{({ style: rotateStyle }) => (
<svg width="20.25" height="14" viewBox="0 0 24 16" style={{ ...style, ...rotateStyle }} {...rest}>
<svg
width="20.25"
height="14"
viewBox="0 0 24 16"
style={{ ...style, ...rotateStyle }}
{...rest}
>
<path
fill={calcFill({ fill, disabled, light, colors })}
d="M13.25 0a7 7 0 0 0-3.13.74A7 7 0 1 0 7 14a7 7 0 0 0 3.12-.74A7 7 0 1 0 13.25 0zM12 7a5 5 0 0 1-1.88 3.87 4.94 4.94 0 0 1 0-7.74A5 5 0 0 1 12 7zM2 7a5 5 0 0 1 5-5 5.26 5.26 0 0 1 1.2.16 7 7 0 0 0 0 9.68A5.26 5.26 0 0 1 7 12a5 5 0 0 1-5-5zm11.25 5a5.26 5.26 0 0 1-1.2-.16 7 7 0 0 0 0-9.68 5.26 5.26 0 0 1 1.2-.16 5 5 0 0 1 0 10z"

View File

@ -15,5 +15,6 @@
</noscript>
<div id="header"></div>
<div id="root"></div>
<script src="/nav-static/main.js"></script>
</body>
</html>

View File

@ -4,8 +4,7 @@ import { Margin, Padding } from 'styled-components-spacing';
import Flex from 'styled-flex-component';
import { H3, Card } from 'joyent-ui-toolkit';
import NoPackagesImage from '../../assets/no-packages.svg';
import NoPackagesImage from '@assets/no-packages.svg';
const NoPackagesTitle = styled(H3)`
color: ${props => props.theme.greyDark};

View File

@ -1,5 +1,3 @@
/* eslint-disable camelcase */
import React, { Fragment } from 'react';
import { Field } from 'redux-form';
import { Margin, Padding } from 'styled-components-spacing';
@ -18,7 +16,7 @@ import {
P
} from 'joyent-ui-toolkit';
import Tag from '@components/instances/tags';
import Tag from '@components/tags';
import Empty from './empty';
const capitalizeFirstLetter = string =>

View File

@ -69,13 +69,13 @@ const Version = styled(Select)`
const getImage = name => {
try {
return {
url: require(`../../assets/${name}.svg`),
url: require(`@assets/${name}.svg`),
size: 42,
bottom: 0
};
} catch (e) {
return {
url: require(`../../assets/placeholder.svg`),
url: require('@assets/placeholder.svg'),
size: 36,
bottom: 6
};

View File

@ -1,71 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders <AddForm /> without throwing 1`] = `
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c22 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c20 {
font-family: sans-serif;
font-size: 100%;
@ -446,6 +381,62 @@ exports[`renders <AddForm /> without throwing 1`] = `
margin-left: 1.75rem;
}
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c22 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c5 {
color: rgba(73,73,73,1);
font-weight: 600;
@ -789,6 +780,15 @@ exports[`renders <AddForm /> without throwing 1`] = `
outline: 0;
}
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c2 {
word-wrap: break-word;
overflow-wrap: break-word;
@ -1011,80 +1011,6 @@ exports[`renders <AddForm /> without throwing 1`] = `
`;
exports[`renders <EditForm /> without throwing 1`] = `
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c18 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c23 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c21 {
font-family: sans-serif;
font-size: 100%;
@ -1465,6 +1391,71 @@ exports[`renders <EditForm /> without throwing 1`] = `
margin-left: 1.75rem;
}
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c18 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c23 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c14 {
background-color: rgb(216,216,216);
margin: 0;
@ -1789,6 +1780,15 @@ exports[`renders <EditForm /> without throwing 1`] = `
outline: 0;
}
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c5 {
word-break: break-all;
line-height: 1.5;

View File

@ -1232,7 +1232,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
name="td"
selected={undefined}
>
about 1 month
about 2 months
</td>
<td
className="c11"
@ -1240,7 +1240,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
name="td"
selected={undefined}
>
about 1 month
about 2 months
</td>
<td
className="c12 c13"

View File

@ -1,17 +0,0 @@
import React from 'react';
import { TableTr, TableTd } from 'joyent-ui-toolkit';
export default ({ rule = '', global = false, enabled = false }) => (
<TableTr>
<TableTd>
<code>{rule}</code>
</TableTd>
<TableTd center middle>
<code>{JSON.stringify(global)}</code>
</TableTd>
<TableTd center middle>
<code>{JSON.stringify(enabled)}</code>
</TableTd>
</TableTr>
);

View File

@ -1,6 +1,3 @@
export { default as List } from './list';
export { default as KeyValue } from './key-value';
export { default as Network } from './network';
export { default as FirewallRule } from './firewall-rule';
export { default as Resize } from './resize';
export { default as Snapshots } from './snapshots';

View File

@ -1,6 +1,5 @@
import React from 'react';
import { Field } from 'redux-form';
import KeyValue from './key-value';
import {
Row,
@ -11,6 +10,8 @@ import {
Button
} from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
export const MenuForm = ({ searchable, onAdd }) => (
<form>
<Row>

View File

@ -1,13 +0,0 @@
import React from 'react';
import forceArray from 'force-array';
import { TableTr, TableTd } from 'joyent-ui-toolkit';
export default ({ name, gateway, subnet, resolvers = [] }) => (
<TableTr>
<TableTd>{name}</TableTd>
<TableTd>{gateway}</TableTd>
<TableTd>{subnet}</TableTd>
<TableTd>{forceArray(resolvers).join('\u00B7')}</TableTd>
</TableTr>
);

View File

@ -3,7 +3,6 @@ import { Field } from 'redux-form';
import titleCase from 'title-case';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import remcalc from 'remcalc';
import { KeyValue } from '@components/instances';
import {
FormGroup,
@ -23,6 +22,8 @@ import {
DotIcon
} from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
const stateColor = {
QUEUED: 'primary',
CREATED: 'green'

View File

@ -0,0 +1,320 @@
import React, { Fragment } from 'react';
import { Field } from 'redux-form';
import { Margin, Padding } from 'styled-components-spacing';
import styled from 'styled-components';
import Flex, { FlexItem } from 'styled-flex-component';
import remcalc from 'remcalc';
import { Row, Col } from 'react-styled-flexboxgrid';
import {
H4,
P,
Small,
DotIcon,
Card,
CardHeader,
CardHeaderMeta,
CardHeaderBox,
CardOutlet,
FormGroup,
FormLabel,
Input,
Checkbox,
Divider,
FabricIcon,
InstanceCountIcon,
DataCenterIcon,
PublicIcon,
PrivateIcon,
ArrowIcon
} from 'joyent-ui-toolkit';
const Box = styled.div`
display: inline-block;
background-color: ${props => props.theme.white};
border: ${remcalc(1)} solid ${props => props.theme.grey};
border-radius: ${remcalc(4)};
min-width: ${remcalc(300)};
`;
export const Collapsed = ({ name, fabric, ...network }) => (
<Row>
<Col xs={12} sm={8}>
<Margin inline right={3} top={3}>
<Box>
<Flex column>
<FlexItem>
<Margin left={3} right={3} top={2} bottom={2}>
<P>{name}</P>
</Margin>
</FlexItem>
<FlexItem>
<Divider height={remcalc(1)} />
</FlexItem>
<FlexItem>
<Margin left={3} right={3} top={2} bottom={2}>
<Flex>
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{network.public ? <PublicIcon /> : <PrivateIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>{network.public ? 'Public' : 'Private'}</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
<Margin>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{fabric ? <FabricIcon /> : <DataCenterIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>
{fabric ? 'Fabric network' : 'Data center network'}
</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
</Flex>
</Margin>
</FlexItem>
</Flex>
</Box>
</Margin>
</Col>
</Row>
);
export const Expanded = ({
id,
name,
fabric,
subnet,
description,
machines = [],
provision_start_ip,
provision_end_ip,
selected = false,
infoExpanded = false,
machinesExpanded = false,
readOnly = false,
onInfoClick,
onMachinesClick,
...network
}) => (
<Row>
<Col xs={12} sm={8}>
<Margin bottom={4}>
<Card shadow>
<CardHeader secondary={selected}>
{!readOnly ? (
<CardHeaderBox>
<FormGroup name={id} field={Field}>
<Checkbox noMargin checked={selected} />
</FormGroup>
</CardHeaderBox>
) : null}
<CardHeaderMeta>
<Padding left={readOnly ? 3 : 0} right={readOnly ? 3 : 0}>
<H4 white={selected}>{name}</H4>
</Padding>
</CardHeaderMeta>
</CardHeader>
<CardOutlet>
<Padding all={4}>
{description && (
<Margin bottom={3}>
<P>{description}</P>
</Margin>
)}
<Flex>
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{network.public ? <PublicIcon /> : <PrivateIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>{network.public ? 'Public' : 'Private'}</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{fabric ? <FabricIcon /> : <DataCenterIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>
{fabric ? 'Fabric network' : 'Data center network'}
</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
{fabric ? (
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
<InstanceCountIcon />
</Margin>
</FlexItem>
<FlexItem>
<P>
{`${machines.length} instance${
machines.length === 1 ? '' : 's'
}`}
</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
) : null}
</Flex>
{fabric ? (
<Fragment>
<Margin top={3}>
<Card collapsed={!infoExpanded} actionable={!infoExpanded}>
<CardHeader
secondary={false}
transparent={false}
onClick={onInfoClick}
>
<CardHeaderMeta>
<Padding left={3} right={3}>
<P>Network information</P>
</Padding>
</CardHeaderMeta>
<CardHeaderBox>
<ArrowIcon direction={infoExpanded ? 'up' : 'down'} />
</CardHeaderBox>
</CardHeader>
{infoExpanded ? (
<CardOutlet>
<Padding all={3}>
<Flex column>
{network.internet_nat ? (
<FlexItem>
<Margin bottom={3}>
<Flex alignCenter>
<Margin right={1}>
<DotIcon
width={remcalc(12)}
height={remcalc(12)}
color="green"
/>
</Margin>
<Small bold noMargin>
Outbound internet access enabled
</Small>
</Flex>
</Margin>
</FlexItem>
) : null}
<FlexItem>
<FormGroup name="id">
<FormLabel>ID</FormLabel>
<Input
onBlur={null}
big
monospace
type="text"
value={id}
/>
</FormGroup>
</FlexItem>
<FlexItem>
<FormGroup name="subnet">
<FormLabel>Subnet</FormLabel>
<Input
onBlur={null}
big
monospace
type="text"
value={subnet}
/>
</FormGroup>
</FlexItem>
<FlexItem>
<FormGroup name="ip-range">
<FormLabel>IP range</FormLabel>
<Input
onBlur={null}
big
monospace
type="text"
value={`${provision_start_ip} - ${provision_end_ip}`}
/>
</FormGroup>
</FlexItem>
</Flex>
</Padding>
</CardOutlet>
) : null}
</Card>
</Margin>
<Margin top={3}>
<Card
collapsed={!machinesExpanded}
actionable={!machinesExpanded}
>
<CardHeader
secondary={false}
transparent={false}
onClick={onMachinesClick}
>
<CardHeaderMeta>
<Padding left={3} right={3}>
<P>Instances on network</P>
</Padding>
</CardHeaderMeta>
<CardHeaderBox>
<ArrowIcon
direction={machinesExpanded ? 'up' : 'down'}
/>
</CardHeaderBox>
</CardHeader>
{machinesExpanded ? (
<CardOutlet>
<Padding all={3}>
<Flex column>
{machines.map(({ name }) => (
<FlexItem>
<P>{name}</P>
</FlexItem>
))}
</Flex>
</Padding>
</CardOutlet>
) : null}
</Card>
</Margin>
</Fragment>
) : null}
</Padding>
</CardOutlet>
</Card>
</Margin>
</Col>
</Row>
);
export default ({ small = false, ...rest }) =>
small ? <Collapsed {...rest} /> : <Expanded {...rest} />;

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Margin } from 'styled-components-spacing';
import { KeyValue } from '@components/instances';
import { TagItem } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
export const AddForm = props => (
<KeyValue {...props} method="add" input="input" type="tag" expanded />

View File

@ -1606,23 +1606,23 @@ Array [
cursor: not-allowed;
}
.c16 {
.c17 {
color: rgba(73,73,73,1);
line-height: 1.5rem;
font-size: 0.9375rem;
margin: 0;
}
.c16 + p,
.c16 + small,
.c16 + h1,
.c16 + h2,
.c16 + label,
.c16 + h3,
.c16 + h4,
.c16 + h5,
.c16 + div,
.c16 + span {
.c17 + p,
.c17 + small,
.c17 + h1,
.c17 + h2,
.c17 + label,
.c17 + h3,
.c17 + h4,
.c17 + h5,
.c17 + div,
.c17 + span {
padding-bottom: 2.25rem;
}
@ -1654,7 +1654,7 @@ Array [
padding-left: 0.5rem;
}
.c12 {
.c13 {
color: rgba(73,73,73,1);
font-weight: 600;
line-height: 1.5rem;
@ -1662,20 +1662,20 @@ Array [
margin: 0;
}
.c12 + p,
.c12 + small,
.c12 + h1,
.c12 + h2,
.c12 + label,
.c12 + h3,
.c12 + h4,
.c12 + h5,
.c12 + div,
.c12 + span {
.c13 + p,
.c13 + small,
.c13 + h1,
.c13 + h2,
.c13 + label,
.c13 + h3,
.c13 + h4,
.c13 + h5,
.c13 + div,
.c13 + span {
margin-top: 0.75rem;
}
.c30 {
.c31 {
color: rgba(73,73,73,1);
font-weight: 600;
line-height: 1.5rem;
@ -1685,16 +1685,16 @@ Array [
color: rgb(255,255,255);
}
.c30 + p,
.c30 + small,
.c30 + h1,
.c30 + h2,
.c30 + label,
.c30 + h3,
.c30 + h4,
.c30 + h5,
.c30 + div,
.c30 + span {
.c31 + p,
.c31 + small,
.c31 + h1,
.c31 + h2,
.c31 + label,
.c31 + h3,
.c31 + h4,
.c31 + h5,
.c31 + div,
.c31 + span {
margin-top: 0.75rem;
}
@ -1872,7 +1872,7 @@ Array [
color: inherit;
}
.c13 {
.c14 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -1908,15 +1908,15 @@ Array [
background-color: transparent;
}
.c13 > [name='card']:not(:last-child) {
.c14 > [name='card']:not(:last-child) {
margin-bottom: 0.8125rem;
}
.c13 > [name='card']:last-child {
.c14 > [name='card']:last-child {
margin-bottom: 0.4375rem;
}
.c23 {
.c24 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -1944,7 +1944,7 @@ Array [
flex: 0 0 2.875rem;
}
.c24 {
.c25 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -1982,12 +1982,12 @@ Array [
box-shadow: none;
}
.c24 button {
.c25 button {
margin-bottom: 0;
margin-top: 0;
}
.c25 {
.c26 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -2046,7 +2046,7 @@ Array [
color: inherit;
}
.c27 {
.c28 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -2084,12 +2084,12 @@ Array [
margin: -0.0625rem -0.0625rem 0 -0.0625rem;
}
.c27 button {
.c28 button {
margin-bottom: 0;
margin-top: 0;
}
.c28 {
.c29 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -2145,7 +2145,7 @@ Array [
box-shadow: none;
}
.c29 {
.c30 {
box-sizing: content-box;
display: -webkit-box;
display: -webkit-flex;
@ -2281,7 +2281,7 @@ Array [
-webkit-padding-after: 0;
}
.c19 {
.c20 {
-webkit-order: 0;
-ms-flex-order: 0;
order: 0;
@ -2297,7 +2297,7 @@ Array [
flex-shrink: 1;
}
.c21 {
.c22 {
margin-right: 0.25rem;
}
@ -2305,28 +2305,33 @@ Array [
margin-bottom: 2rem;
}
.c15 {
.c16 {
margin-bottom: 1rem;
}
.c18 {
.c19 {
margin-right: 2rem;
}
.c22 {
.c23 {
margin-top: 1rem;
}
.c14 {
.c12 {
padding-right: 0;
padding-left: 0;
}
.c15 {
padding: 2rem;
}
.c26 {
.c27 {
padding-right: 1rem;
padding-left: 1rem;
}
.c17 {
.c18 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@ -2346,7 +2351,7 @@ Array [
align-content: stretch;
}
.c20 {
.c21 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@ -2453,32 +2458,33 @@ Array [
disabled={false}
name="card-header-meta"
>
<h4
<div
className="c12"
>
<h4
className="c13"
>
name
</h4>
</div>
</div>
<div
className="c13"
name="card-outlet"
>
</div>
<div
className="c14"
name="card-outlet"
>
<div
className="c15"
>
<p
<div
className="c16"
>
<p
className="c17"
>
description
</p>
</div>
<div
className="c17"
>
<div
className="c18"
>
@ -2489,10 +2495,13 @@ Array [
className="c20"
>
<div
className="c19"
className="c21"
>
<div
className="c21"
className="c20"
>
<div
className="c22"
>
<svg
className=""
@ -2515,10 +2524,10 @@ Array [
</div>
</div>
<div
className="c19"
className="c20"
>
<p
className="c16"
className="c17"
>
Private
</p>
@ -2526,9 +2535,6 @@ Array [
</div>
</div>
</div>
<div
className="c18"
>
<div
className="c19"
>
@ -2536,10 +2542,13 @@ Array [
className="c20"
>
<div
className="c19"
className="c21"
>
<div
className="c21"
className="c20"
>
<div
className="c22"
>
<svg
height="16.2"
@ -2555,10 +2564,10 @@ Array [
</div>
</div>
<div
className="c19"
className="c20"
>
<p
className="c16"
className="c17"
>
Fabric network
</p>
@ -2566,31 +2575,165 @@ Array [
</div>
</div>
</div>
</div>
<div
className="c19"
>
<div
className="c20"
>
<div
className="c21"
>
<div
className="c20"
>
<div
className="c22"
>
<svg
className=""
height="17"
innerRef={undefined}
style={
Object {
"transform": "rotate(0deg)",
}
}
version="1.1"
viewBox="0 0 17 17"
width="17"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<g
transform="translate(-1105 974)"
>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1105 -974)"
xlinkHref="#c"
/>
</g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1111 -974)"
xlinkHref="#c"
/>
</g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1117 -974)"
xlinkHref="#c"
/>
</g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1105 -968)"
xlinkHref="#c"
/>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1111 -968)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1117 -968)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1105 -962)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1111 -962)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1117 -962)"
xlinkHref="#d"
/>
</g>
</g>
</g>
</g>
<defs>
<path
d="M 2.5 5C 3.88071 5 5 3.88071 5 2.5C 5 1.11929 3.88071 0 2.5 0C 1.11929 0 0 1.11929 0 2.5C 0 3.88071 1.11929 5 2.5 5Z"
id="c"
/>
<path
d="M 2.5 1C 3.3 1 4 1.7 4 2.5C 4 3.3 3.3 4 2.5 4C 1.7 4 1 3.3 1 2.5C 1 1.7 1.7 1 2.5 1ZM 2.5 0C 1.1 0 0 1.1 0 2.5C 0 3.9 1.1 5 2.5 5C 3.9 5 5 3.9 5 2.5C 5 1.1 3.9 0 2.5 0Z"
id="d"
/>
</defs>
</svg>
</div>
</div>
<div
className="c20"
>
<p
className="c17"
>
0 instances
</p>
</div>
</div>
</div>
</div>
</div>
<div
className="c23"
>
<div
className="c24"
disabled={false}
name="card"
>
<div
className="c24"
className="c25"
disabled={false}
name="card-header"
onClick={[Function]}
>
<div
className="c25"
className="c26"
disabled={false}
name="card-header-meta"
>
<div
className="c26"
className="c27"
>
<p
className="c16"
className="c17"
>
Network information
</p>
@ -2623,6 +2766,62 @@ Array [
</div>
</div>
</div>
<div
className="c23"
>
<div
className="c24"
disabled={false}
name="card"
>
<div
className="c25"
disabled={false}
name="card-header"
onClick={[Function]}
>
<div
className="c26"
disabled={false}
name="card-header-meta"
>
<div
className="c27"
>
<p
className="c17"
>
Instances on network
</p>
</div>
</div>
<div
className="c5"
disabled={false}
name="card-header-box"
>
<svg
className=""
height="6"
innerRef={undefined}
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 9.6 6"
width="9.6"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.6,1.12,8.24,0,4.8,3.5,1.36,0,0,1.12,4.8,6Z"
fill="rgba(73, 73, 73, 1)"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@ -2644,12 +2843,12 @@ Array [
name="card"
>
<div
className="c27"
className="c28"
disabled={false}
name="card-header"
>
<div
className="c28"
className="c29"
disabled={false}
name="card-header-box"
>
@ -2687,36 +2886,37 @@ Array [
</div>
</div>
<div
className="c29"
className="c30"
disabled={false}
name="card-header-meta"
>
<div
className="c12"
>
<h4
className="c30"
className="c31"
>
name2
</h4>
</div>
</div>
<div
className="c13"
name="card-outlet"
>
</div>
<div
className="c14"
name="card-outlet"
>
<div
className="c15"
>
<p
<div
className="c16"
>
<p
className="c17"
>
description2
</p>
</div>
<div
className="c17"
>
<div
className="c18"
>
@ -2727,10 +2927,13 @@ Array [
className="c20"
>
<div
className="c19"
className="c21"
>
<div
className="c21"
className="c20"
>
<div
className="c22"
>
<svg
className=""
@ -2753,10 +2956,10 @@ Array [
</div>
</div>
<div
className="c19"
className="c20"
>
<p
className="c16"
className="c17"
>
Private
</p>
@ -2764,9 +2967,6 @@ Array [
</div>
</div>
</div>
<div
className="c18"
>
<div
className="c19"
>
@ -2774,10 +2974,13 @@ Array [
className="c20"
>
<div
className="c19"
className="c21"
>
<div
className="c21"
className="c20"
>
<div
className="c22"
>
<svg
height="13"
@ -2793,10 +2996,10 @@ Array [
</div>
</div>
<div
className="c19"
className="c20"
>
<p
className="c16"
className="c17"
>
Data center network
</p>
@ -2887,6 +3090,11 @@ Array [
background-color: rgb(59,70,204);
border-radius: 0.25rem;
border: solid 0.0625rem rgb(45,56,132);
cursor: not-allowed;
pointer-events: none;
color: rgb(216,216,216);
background-color: rgb(250,250,250);
border-color: rgb(216,216,216);
}
.c1:focus {
@ -2915,6 +3123,23 @@ Array [
pointer-events: none;
}
.c1:focus {
background-color: rgb(250,250,250);
border-color: rgb(216,216,216);
}
.c1:hover {
background-color: rgb(250,250,250);
border-color: rgb(250,250,250);
}
.c1:active,
.c1:active:hover,
.c1:active:focus {
background-color: rgb(250,250,250);
border-color: rgb(250,250,250);
}
.c0 {
margin-bottom: 2rem;
}
@ -2924,7 +3149,7 @@ Array [
>
<button
className="c1 c2 c3"
disabled={false}
disabled={true}
href=""
onClick={undefined}
type="button"
@ -3105,9 +3330,9 @@ Array [
<h3
className="c0"
>
1
0
network
s
added
</h3>,
.c7 {

View File

@ -7776,35 +7776,6 @@ Array [
border-color: rgb(216,216,216);
}
.c5 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c4 {
background-color: rgb(216,216,216);
margin: 0;
height: 0.0625rem;
}
.c3 {
margin-top: 2rem;
}
<div>
<button
className="c0 c1 c2"
@ -7814,14 +7785,6 @@ Array [
>
Edit
</button>
<div
className="c3"
>
<div
className="c4 c5"
height="0.0625rem"
/>
</div>
</div>,
]
`;

View File

@ -84,7 +84,7 @@ it('renders <UserScript expanded edit formOpen script /> without throwing', () =
renderer
.create(
<Theme>
<UserScript expanded edit formOpen script={{ value: 'hey' }}/>
<UserScript expanded edit formOpen script={{ value: 'hey' }} />
</Theme>
)
.toJSON()

View File

@ -12,8 +12,8 @@ import { AffinityIcon, Button, H3, Divider } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title';
import { Rule, Header } from '@components/create-instance/affinity';
import KeyValue from '@components/instances/key-value';
import Description from '@components/create-instance/description';
import Description from '@components/description';
import KeyValue from '@components/key-value';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-AFFINITY-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-AFFINITY-EDIT-${i}`;

View File

@ -24,8 +24,6 @@ import {
StatusLoader
} from 'joyent-ui-toolkit';
import getAccount from '../../graphql/get-account.gql';
import {
Hostname,
Header,
@ -33,9 +31,10 @@ import {
HostnamesHeader
} from '@components/create-instance/cns';
import Tag from '@components/tags';
import Title from '@components/create-instance/title';
import Tag from '@components/instances/tags';
import Description from '@components/create-instance/description';
import Description from '@components/description';
import getAccount from '@graphql/get-account.gql';
const CNS_FORM = 'create-instance-cns';

View File

@ -9,13 +9,13 @@ import { connect } from 'react-redux';
import get from 'lodash.get';
import forceArray from 'force-array';
import { StatusLoader, FirewallIcon, H3, Button } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description';
import Description from '@components/description';
import FirewallForm from '@components/create-instance/firewall';
import ListFwRules from '@graphql/list-fw-rules.gql';
import { StatusLoader, FirewallIcon, H3, Button } from 'joyent-ui-toolkit';
const FORM_NAME = 'CREATE-INSTANCE-FIREWALL';
const Firewall = ({

View File

@ -6,12 +6,12 @@ import { set } from 'react-redux-values';
import get from 'lodash.get';
import { InstanceTypeIcon, StatusLoader } from 'joyent-ui-toolkit';
import Description from '@components/description';
import Image, { Preview } from '@components/create-instance/image';
import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description';
import imageData from '../../data/images-map.json';
import getImages from '../../graphql/get-images.gql';
import imageData from '@data/images-map.json';
import getImages from '@graphql/get-images.gql';
const ImageContainer = ({
expanded,

View File

@ -10,8 +10,8 @@ import get from 'lodash.get';
import { MetadataIcon, Button, H3 } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description';
import KeyValue from '@components/instances/key-value';
import Description from '@components/description';
import KeyValue from '@components/key-value';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-METADATA-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-METADATA-EDIT-${i}`;
@ -75,7 +75,8 @@ export const Metadata = ({
input="textarea"
type="metadata"
onToggleExpanded={() =>
expanded ? handleToggleExpanded(index) : null}
expanded ? handleToggleExpanded(index) : null
}
onCancel={() => handleCancelEdit(index)}
onRemove={() => handleRemoveMetadata(index)}
/>
@ -138,7 +139,7 @@ export default compose(
set({ name: 'create-instance-metadata-proceeded', value: true })
);
return history.push(`/instances/~create/user-script`);
return history.push('/instances/~create/user-script');
},
handleEdit: () => {
return history.push(`/instances/~create/metadata`);

View File

@ -12,7 +12,7 @@ import punycode from 'punycode';
import { NameIcon, H3, Button } from 'joyent-ui-toolkit';
import Name from '@components/create-instance/name';
import Description from '@components/create-instance/description';
import Description from '@components/description';
import Title from '@components/create-instance/title';
import GetInstance from '@graphql/get-instance-small.gql';
import GetRandomName from '@graphql/get-random-name.gql';

View File

@ -9,25 +9,24 @@ import forceArray from 'force-array';
import { NetworkIcon, Button, H3, StatusLoader } from 'joyent-ui-toolkit';
import Description from '@components/create-instance/description';
import Network from '@components/network';
import Description from '@components/description';
import Title from '@components/create-instance/title';
import Network from '@components/create-instance/network';
import ListNetworks from '@graphql/list-networks.gql';
const FORM_NAME = 'CREATE-INSTANCE-NETWORKS';
export const Networks = ({
networks = [],
selected = [],
expanded = false,
proceeded = false,
loading = false,
setInfoExpanded,
setMachinesExpanded,
handleNext,
handleEdit
}) => {
const selected = networks.filter(({ selected }) => selected);
return (
}) => (
<Fragment>
<Title
onClick={!expanded && !proceeded && handleEdit}
@ -37,11 +36,10 @@ export const Networks = ({
</Title>
{expanded ? (
<Description>
Instances are automatically connected to a private fabric network,
which is the best choice for internal communication within your
application. Data center networks are the best choice for exposing
your application to the public internet (if the data center network is
a public network).{' '}
Instances are automatically connected to a private fabric network, which
is the best choice for internal communication within your application.
Data center networks are the best choice for exposing your application
to the public internet (if the data center network is a public network).{' '}
<a
target="__blank"
href="https://docs.joyent.com/public-cloud/network/sdn"
@ -65,15 +63,19 @@ export const Networks = ({
{props => (
<form>
{networks.map(
({ id, selected, infoExpanded, ...network }) =>
({ id, selected, infoExpanded, machinesExpanded, ...network }) =>
!expanded && !selected ? null : (
<Network
key={id}
id={id}
selected={selected}
infoExpanded={infoExpanded}
machinesExpanded={machinesExpanded}
small={!expanded && selected}
onInfoClick={() => setInfoExpanded(id, !infoExpanded)}
onMachinesClick={() =>
setMachinesExpanded(id, !machinesExpanded)
}
{...network}
/>
)
@ -84,11 +86,7 @@ export const Networks = ({
) : null}
<Margin bottom={4}>
{expanded ? (
<Button
type="button"
disabled={!selected.length}
onClick={handleNext}
>
<Button type="button" disabled={!selected.length} onClick={handleNext}>
Next
</Button>
) : proceeded ? (
@ -98,8 +96,7 @@ export const Networks = ({
) : null}
</Margin>
</Fragment>
);
};
);
export default compose(
graphql(ListNetworks, {
@ -119,9 +116,7 @@ export default compose(
const selected = get(form, `${FORM_NAME}.values`, {});
const empty = id => !Object.keys(selected).includes(id);
return {
proceeded: get(values, 'create-instance-networks-proceeded', false),
networks: networks
const _networks = networks
.map(({ id, name, ...network }) => ({
...network,
name,
@ -131,12 +126,22 @@ export default compose(
: Boolean(selected[id]),
infoExpanded: get(
values,
`create-instance-networks-${id}-expanded`,
`create-instance-networks-${id}-info-expanded`,
false
),
machinesExpanded: get(
values,
`create-instance-networks-${id}-machines-expanded`,
false
),
id
}))
.sort((a, b) => a.name < b.name)
.sort((a, b) => a.name < b.name);
return {
proceeded: get(values, 'create-instance-networks-proceeded', false),
selected: _networks.filter(({ selected }) => selected),
networks: _networks
};
},
(dispatch, { history }) => ({
@ -153,7 +158,15 @@ export default compose(
setInfoExpanded: (id, expanded) => {
return dispatch(
set({
name: `create-instance-networks-${id}-expanded`,
name: `create-instance-networks-${id}-info-expanded`,
value: expanded
})
);
},
setMachinesExpanded: (id, expanded) => {
return dispatch(
set({
name: `create-instance-networks-${id}-machines-expanded`,
value: expanded
})
);

View File

@ -11,17 +11,18 @@ import constantCase from 'constant-case';
import { reset } from 'redux-form';
import { PackageIcon, StatusLoader } from 'joyent-ui-toolkit';
import {
Filters,
Packages,
Package,
Overview
} from '@components/create-instance/package';
import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description';
import priceData from '../../data/prices.json';
import getPackages from '../../graphql/get-packages.gql';
import Title from '@components/create-instance/title';
import Description from '@components/description';
import getPackages from '@graphql/get-packages.gql';
import priceData from '@data/prices.json';
const FORM_NAME = 'create-instance-package';
const FILTERS = 'create-instance-package-filters';

View File

@ -10,9 +10,9 @@ import get from 'lodash.get';
import { TagsIcon, Button, H3, TagList } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title';
import Tag from '@components/instances/tags';
import KeyValue from '@components/instances/key-value';
import Description from '@components/create-instance/description';
import KeyValue from '@components/key-value';
import Description from '@components/description';
import Tag from '@components/tags';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-TAGS-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-TAGS-EDIT-${i}`;

View File

@ -6,12 +6,11 @@ import { destroy } from 'redux-form';
import { Margin } from 'styled-components-spacing';
import { connect } from 'react-redux';
import get from 'lodash.get';
import remcalc from 'remcalc';
import { ScriptIcon, Button, Divider } from 'joyent-ui-toolkit';
import { ScriptIcon, Button } from 'joyent-ui-toolkit';
import KeyValue from '@components/instances/key-value';
import Description from '@components/create-instance/description';
import KeyValue from '@components/key-value';
import Description from '@components/description';
import Title from '@components/create-instance/title';
const FORM_NAME = 'create-instance-user-script';
@ -84,9 +83,6 @@ export const UserScript = ({
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>
<Margin top={4}>
<Divider height={remcalc(1)} />
</Margin>
</Fragment>
) : null}
</div>

View File

@ -0,0 +1,76 @@
import React from 'react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';
import { Networks } from '../networks';
import Theme from '@mocks/theme';
it('renders <Networks /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Networks />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Networks loading /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Networks loading />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Networks error /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Networks error />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Networks networks /> without throwing', () => {
const networks = [
{
id: '1',
name: 'name',
description: 'description',
fabric: true,
subnet: '255.255.255.0',
provision_start_ip: '192.168.1.2',
provision_end_ip: '192.168.1.253',
machines: [{ name: 'hello' }, { name: 'hello2' }]
},
{
id: '2',
name: 'name2',
description: 'description2',
fabric: false,
subnet: '255.255.255.0',
provision_start_ip: '192.168.1.2',
provision_end_ip: '192.168.1.253',
}
];
expect(
renderer
.create(
<Theme>
<Networks networks={networks} />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});

View File

@ -1,100 +1,104 @@
import React from 'react';
import PropTypes from 'prop-types';
import forceArray from 'force-array';
import { compose, graphql } from 'react-apollo';
import find from 'lodash.find';
import get from 'lodash.get';
import {
ViewContainer,
StatusLoader,
Message,
MessageDescription,
MessageTitle,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
P
} from 'joyent-ui-toolkit';
export default () => <p>firewalls</p>;
import GetFirewallRules from '@graphql/list-firewall-rules.gql';
import { FirewallRule as InstanceFirewallRule } from '@components/instances';
const Firewall = ({
// eslint-disable-next-line camelcase
firewallEnabled = false,
firewallRules = [],
loading,
error
}) => {
const values = forceArray(firewallRules);
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
const _firewall =
_loading && !values.length ? null : (
<Table>
<TableThead>
<TableTr>
<TableTh left bottom>
<P>Rule</P>
</TableTh>
<TableTh xs="63" center bottom>
<P>Global</P>
</TableTh>
<TableTh xs="75" center bottom>
<P>Enabled</P>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{values.map(network => (
<InstanceFirewallRule key={network.id} {...network} />
))}
</TableTbody>
</Table>
);
const _error =
error && !values.length && !_loading ? (
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
An error occurred while loading your instance firewall rules
</MessageDescription>
</Message>
) : null;
return (
<ViewContainer center={Boolean(_loading)} main>
{_loading}
{_error}
{_firewall}
</ViewContainer>
);
};
Firewall.propTypes = {
loading: PropTypes.bool
};
export default compose(
graphql(GetFirewallRules, {
options: ({ match }) => ({
pollInterval: 1000,
variables: {
name: get(match, 'params.instance')
}
}),
props: ({ data: { loading, error, variables, ...rest } }) => {
const machine = find(get(rest, 'machines', []), ['name', variables.name]);
// eslint-disable-next-line camelcase
const firewallEnabled = get(machine, 'firewall_enabled', false);
const firewallRules = get(machine, 'firewall_rules', []);
// eslint-disable-next-line camelcase
return { firewallEnabled, firewallRules, loading, error };
}
})
)(Firewall);
//
// import PropTypes from 'prop-types';
// import forceArray from 'force-array';
// import { compose, graphql } from 'react-apollo';
// import find from 'lodash.find';
// import get from 'lodash.get';
//
// import {
// ViewContainer,
// StatusLoader,
// Message,
// MessageDescription,
// MessageTitle,
// Table,
// TableThead,
// TableTr,
// TableTh,
// TableTbody,
// P
// } from 'joyent-ui-toolkit';
//
// import GetFirewallRules from '@graphql/list-firewall-rules.gql';
// import { FirewallRule as InstanceFirewallRule } from '@components/instances';
//
// const Firewall = ({
// // eslint-disable-next-line camelcase
// firewallEnabled = false,
// firewallRules = [],
// loading,
// error
// }) => {
// const values = forceArray(firewallRules);
// const _loading = !(loading && !values.length) ? null : <StatusLoader />;
//
// const _firewall =
// _loading && !values.length ? null : (
// <Table>
// <TableThead>
// <TableTr>
// <TableTh left bottom>
// <P>Rule</P>
// </TableTh>
// <TableTh xs="63" center bottom>
// <P>Global</P>
// </TableTh>
// <TableTh xs="75" center bottom>
// <P>Enabled</P>
// </TableTh>
// </TableTr>
// </TableThead>
// <TableTbody>
// {values.map(network => (
// <InstanceFirewallRule key={network.id} {...network} />
// ))}
// </TableTbody>
// </Table>
// );
//
// const _error =
// error && !values.length && !_loading ? (
// <Message error>
// <MessageTitle>Ooops!</MessageTitle>
// <MessageDescription>
// An error occurred while loading your instance firewall rules
// </MessageDescription>
// </Message>
// ) : null;
//
// return (
// <ViewContainer center={Boolean(_loading)} main>
// {_loading}
// {_error}
// {_firewall}
// </ViewContainer>
// );
// };
//
// Firewall.propTypes = {
// loading: PropTypes.bool
// };
//
// export default compose(
// graphql(GetFirewallRules, {
// options: ({ match }) => ({
// pollInterval: 1000,
// variables: {
// name: get(match, 'params.instance')
// }
// }),
// props: ({ data: { loading, error, variables, ...rest } }) => {
// const machine = find(get(rest, 'machines', []), ['name', variables.name]);
// // eslint-disable-next-line camelcase
// const firewallEnabled = get(machine, 'firewall_enabled', false);
// const firewallRules = get(machine, 'firewall_rules', []);
//
// // eslint-disable-next-line camelcase
// return { firewallEnabled, firewallRules, loading, error };
// }
// })
// )(Firewall);

View File

@ -1,97 +1,121 @@
import React from 'react';
import PropTypes from 'prop-types';
import forceArray from 'force-array';
import { compose, graphql } from 'react-apollo';
import { connect } from 'react-redux';
import { set } from 'react-redux-values';
import forceArray from 'force-array';
import { Margin } from 'styled-components-spacing';
import find from 'lodash.find';
import sortBy from 'lodash.sortby';
import get from 'lodash.get';
import {
ViewContainer,
StatusLoader,
Message,
MessageDescription,
MessageTitle,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
P
MessageDescription,
H3
} from 'joyent-ui-toolkit';
import GetNetworks from '@graphql/list-networks.gql';
import { Network as InstanceNetwork } from '@components/instances';
import Network from '@components/network';
import Description from '@components/description';
import GetNetworks from '@graphql/list-instance-networks.gql';
const Networks = ({ networks = [], loading, error }) => {
const values = forceArray(networks);
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
const _networks =
_loading && !values.length ? null : (
<Table>
<TableThead>
<TableTr>
<TableTh left bottom>
<P>Name</P>
</TableTh>
<TableTh xs="90" left bottom>
<P>Gateway</P>
</TableTh>
<TableTh xs="90" left bottom>
<P>Subnet</P>
</TableTh>
<TableTh xs="90" left bottom>
<P>Resolvers</P>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{values.map(network => (
<InstanceNetwork key={network.id} {...network} />
))}
</TableTbody>
</Table>
);
const _error =
error && !values.length && !_loading ? (
export const Networks = ({
networks = [],
loading = false,
error = null,
setMachinesExpanded,
setInfoExpanded
}) => (
<ViewContainer main>
<Margin bottom={1}>
<Description>
Use predefined or customized fabric networks which can be public-facing
or private. All fabrics are isolated from other customers giving you
complete control over the network environment. Read more on fabrics.{' '}
<a
target="__blank"
href="https://docs.joyent.com/public-cloud/network/sdn"
>
Read more
</a>
</Description>
</Margin>
<H3>Networks attached to this instance</H3>
{loading ? <StatusLoader /> : null}
{!loading && error ? (
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
An error occurred while loading your instance networks
An error occurred while loading your networks
</MessageDescription>
</Message>
) : null;
return (
<ViewContainer center={Boolean(_loading)} main>
{_loading}
{_error}
{_networks}
) : null}
{!loading &&
networks.map(({ id, infoExpanded, machinesExpanded, ...network }) => (
<Network
{...network}
key={id}
id={id}
infoExpanded={infoExpanded}
machinesExpanded={machinesExpanded}
onInfoClick={() => setInfoExpanded(id, !infoExpanded)}
onMachinesClick={() => setMachinesExpanded(id, !machinesExpanded)}
selected
readOnly
/>
))}
</ViewContainer>
);
};
Networks.propTypes = {
loading: PropTypes.bool
};
);
export default compose(
graphql(GetNetworks, {
options: ({ match }) => ({
pollInterval: 1000,
variables: {
name: get(match, 'params.instance')
}
}),
props: ({ data: { loading, error, variables, ...rest } }) => ({
networks: get(
find(get(rest, 'machines', []), ['name', variables.name]),
'networks',
[]
),
props: ({ data }) => {
const { machines, loading, error, variables } = data;
const { name } = variables;
const instance = find(forceArray(machines), ['name', name]);
const values = get(instance, 'networks', []);
const networks = sortBy(values, 'public').reverse();
return {
networks,
instance,
loading,
error
};
}
}),
connect(
({ values }, { networks }) => ({
networks: networks.map(({ id, ...network }) => ({
...network,
infoExpanded: get(values, `networks-${id}-info-expanded`, false),
machinesExpanded: get(
values,
`networks-${id}-machines-expanded`,
false
),
id
}))
}),
dispatch => ({
setMachinesExpanded: (id, expanded) => {
return dispatch(
set({ name: `networks-${id}-machines-expanded`, value: expanded })
);
},
setInfoExpanded: (id, expanded) => {
return dispatch(
set({ name: `networks-${id}-info-expanded`, value: expanded })
);
}
})
})
)
)(Networks);

View File

@ -18,20 +18,20 @@ import {
StatusLoader
} from 'joyent-ui-toolkit';
import GetSnapshots from '@graphql/list-snapshots.gql';
import StartSnapshot from '@graphql/start-from-snapshot.gql';
import RemoveSnapshot from '@graphql/remove-snapshot.gql';
import CreateSnapshotMutation from '@graphql/create-snapshot.gql';
import GenIndex from '@state/gen-index';
import ToolbarForm from '@components/instances/toolbar';
import SnapshotsListActions from '@components/instances/footer';
import parseError from '@state/parse-error';
import {
default as SnapshotsList,
AddForm as SnapshotAddForm
} from '@components/instances/snapshots';
import GetSnapshots from '@graphql/list-snapshots.gql';
import StartSnapshot from '@graphql/start-from-snapshot.gql';
import RemoveSnapshot from '@graphql/remove-snapshot.gql';
import CreateSnapshotMutation from '@graphql/create-snapshot.gql';
import ToolbarForm from '@components/instances/toolbar';
import SnapshotsListActions from '@components/instances/footer';
import parseError from '@state/parse-error';
import GenIndex from '@state/gen-index';
const MENU_FORM_NAME = 'snapshot-list-menu';
const TABLE_FORM_NAME = 'snapshot-list-table';
const CREATE_FORM_NAME = 'create-snapshot-form';

View File

@ -23,14 +23,14 @@ import {
default as Tag,
AddForm as TagsAddForm,
EditForm as TagsEditForm
} from '@components/instances/tags';
} from '@components/tags';
import ToolbarForm from '@components/instances/toolbar';
import GetTags from '@graphql/list-tags.gql';
import UpdateTags from '@graphql/update-tags.gql';
import DeleteTag from '@graphql/delete-tag.gql';
import Index from '@state/gen-index';
import parseError from '@state/parse-error';
import ToolbarForm from '@components/instances/toolbar';
const MENU_FORM_NAME = 'instance-tags-list-menu';
const ADD_FORM_NAME = 'instance-tags-add-new';

View File

@ -0,0 +1,23 @@
query instance($name: String!) {
machines(name: $name) {
id
name
networks {
id
name
public
fabric
description
subnet
provision_start_ip
provision_end_ip
gateway
resolvers
internet_nat
machines {
id
name
}
}
}
}

View File

@ -11,5 +11,9 @@ query {
gateway
resolvers
internet_nat
machines {
id
name
}
}
}

View File

@ -22,7 +22,7 @@ export const client = new ApolloClient({
const initialState = {
ui: {
sections: {
instances: ['summary', 'tags', 'metadata', 'snapshots']
instances: ['summary', 'tags', 'metadata', 'networks', 'snapshots']
}
}
};