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}> <Rotate direction={direction}>
{({ style: rotateStyle }) => ( {({ 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 <path
fill={calcFill({ fill, disabled, light, colors })} 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" 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> </noscript>
<div id="header"></div> <div id="header"></div>
<div id="root"></div> <div id="root"></div>
<script src="/nav-static/main.js"></script>
</body> </body>
</html> </html>

View File

@ -4,8 +4,7 @@ import { Margin, Padding } from 'styled-components-spacing';
import Flex from 'styled-flex-component'; import Flex from 'styled-flex-component';
import { H3, Card } from 'joyent-ui-toolkit'; 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)` const NoPackagesTitle = styled(H3)`
color: ${props => props.theme.greyDark}; color: ${props => props.theme.greyDark};

View File

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

View File

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

View File

@ -1,71 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders <AddForm /> without throwing 1`] = ` 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 { .c20 {
font-family: sans-serif; font-family: sans-serif;
font-size: 100%; font-size: 100%;
@ -446,6 +381,62 @@ exports[`renders <AddForm /> without throwing 1`] = `
margin-left: 1.75rem; 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 { .c5 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
font-weight: 600; font-weight: 600;
@ -789,6 +780,15 @@ exports[`renders <AddForm /> without throwing 1`] = `
outline: 0; outline: 0;
} }
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c2 { .c2 {
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
@ -1011,80 +1011,6 @@ exports[`renders <AddForm /> without throwing 1`] = `
`; `;
exports[`renders <EditForm /> 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 { .c21 {
font-family: sans-serif; font-family: sans-serif;
font-size: 100%; font-size: 100%;
@ -1465,6 +1391,71 @@ exports[`renders <EditForm /> without throwing 1`] = `
margin-left: 1.75rem; 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 { .c14 {
background-color: rgb(216,216,216); background-color: rgb(216,216,216);
margin: 0; margin: 0;
@ -1789,6 +1780,15 @@ exports[`renders <EditForm /> without throwing 1`] = `
outline: 0; outline: 0;
} }
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c5 { .c5 {
word-break: break-all; word-break: break-all;
line-height: 1.5; line-height: 1.5;

View File

@ -1232,7 +1232,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
name="td" name="td"
selected={undefined} selected={undefined}
> >
about 1 month about 2 months
</td> </td>
<td <td
className="c11" className="c11"
@ -1240,7 +1240,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
name="td" name="td"
selected={undefined} selected={undefined}
> >
about 1 month about 2 months
</td> </td>
<td <td
className="c12 c13" 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 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 Resize } from './resize';
export { default as Snapshots } from './snapshots'; export { default as Snapshots } from './snapshots';

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { Field } from 'redux-form'; import { Field } from 'redux-form';
import KeyValue from './key-value';
import { import {
Row, Row,
@ -11,6 +10,8 @@ import {
Button Button
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
export const MenuForm = ({ searchable, onAdd }) => ( export const MenuForm = ({ searchable, onAdd }) => (
<form> <form>
<Row> <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 titleCase from 'title-case';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import remcalc from 'remcalc'; import remcalc from 'remcalc';
import { KeyValue } from '@components/instances';
import { import {
FormGroup, FormGroup,
@ -23,6 +22,8 @@ import {
DotIcon DotIcon
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
const stateColor = { const stateColor = {
QUEUED: 'primary', QUEUED: 'primary',
CREATED: 'green' 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 React from 'react';
import { Margin } from 'styled-components-spacing'; import { Margin } from 'styled-components-spacing';
import { KeyValue } from '@components/instances';
import { TagItem } from 'joyent-ui-toolkit'; import { TagItem } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
export const AddForm = props => ( export const AddForm = props => (
<KeyValue {...props} method="add" input="input" type="tag" expanded /> <KeyValue {...props} method="add" input="input" type="tag" expanded />

View File

@ -1606,23 +1606,23 @@ Array [
cursor: not-allowed; cursor: not-allowed;
} }
.c16 { .c17 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
line-height: 1.5rem; line-height: 1.5rem;
font-size: 0.9375rem; font-size: 0.9375rem;
margin: 0; margin: 0;
} }
.c16 + p, .c17 + p,
.c16 + small, .c17 + small,
.c16 + h1, .c17 + h1,
.c16 + h2, .c17 + h2,
.c16 + label, .c17 + label,
.c16 + h3, .c17 + h3,
.c16 + h4, .c17 + h4,
.c16 + h5, .c17 + h5,
.c16 + div, .c17 + div,
.c16 + span { .c17 + span {
padding-bottom: 2.25rem; padding-bottom: 2.25rem;
} }
@ -1654,7 +1654,7 @@ Array [
padding-left: 0.5rem; padding-left: 0.5rem;
} }
.c12 { .c13 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
font-weight: 600; font-weight: 600;
line-height: 1.5rem; line-height: 1.5rem;
@ -1662,20 +1662,20 @@ Array [
margin: 0; margin: 0;
} }
.c12 + p, .c13 + p,
.c12 + small, .c13 + small,
.c12 + h1, .c13 + h1,
.c12 + h2, .c13 + h2,
.c12 + label, .c13 + label,
.c12 + h3, .c13 + h3,
.c12 + h4, .c13 + h4,
.c12 + h5, .c13 + h5,
.c12 + div, .c13 + div,
.c12 + span { .c13 + span {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
.c30 { .c31 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
font-weight: 600; font-weight: 600;
line-height: 1.5rem; line-height: 1.5rem;
@ -1685,16 +1685,16 @@ Array [
color: rgb(255,255,255); color: rgb(255,255,255);
} }
.c30 + p, .c31 + p,
.c30 + small, .c31 + small,
.c30 + h1, .c31 + h1,
.c30 + h2, .c31 + h2,
.c30 + label, .c31 + label,
.c30 + h3, .c31 + h3,
.c30 + h4, .c31 + h4,
.c30 + h5, .c31 + h5,
.c30 + div, .c31 + div,
.c30 + span { .c31 + span {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
@ -1872,7 +1872,7 @@ Array [
color: inherit; color: inherit;
} }
.c13 { .c14 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -1908,15 +1908,15 @@ Array [
background-color: transparent; background-color: transparent;
} }
.c13 > [name='card']:not(:last-child) { .c14 > [name='card']:not(:last-child) {
margin-bottom: 0.8125rem; margin-bottom: 0.8125rem;
} }
.c13 > [name='card']:last-child { .c14 > [name='card']:last-child {
margin-bottom: 0.4375rem; margin-bottom: 0.4375rem;
} }
.c23 { .c24 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -1944,7 +1944,7 @@ Array [
flex: 0 0 2.875rem; flex: 0 0 2.875rem;
} }
.c24 { .c25 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -1982,12 +1982,12 @@ Array [
box-shadow: none; box-shadow: none;
} }
.c24 button { .c25 button {
margin-bottom: 0; margin-bottom: 0;
margin-top: 0; margin-top: 0;
} }
.c25 { .c26 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2046,7 +2046,7 @@ Array [
color: inherit; color: inherit;
} }
.c27 { .c28 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2084,12 +2084,12 @@ Array [
margin: -0.0625rem -0.0625rem 0 -0.0625rem; margin: -0.0625rem -0.0625rem 0 -0.0625rem;
} }
.c27 button { .c28 button {
margin-bottom: 0; margin-bottom: 0;
margin-top: 0; margin-top: 0;
} }
.c28 { .c29 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2145,7 +2145,7 @@ Array [
box-shadow: none; box-shadow: none;
} }
.c29 { .c30 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2281,7 +2281,7 @@ Array [
-webkit-padding-after: 0; -webkit-padding-after: 0;
} }
.c19 { .c20 {
-webkit-order: 0; -webkit-order: 0;
-ms-flex-order: 0; -ms-flex-order: 0;
order: 0; order: 0;
@ -2297,7 +2297,7 @@ Array [
flex-shrink: 1; flex-shrink: 1;
} }
.c21 { .c22 {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
@ -2305,28 +2305,33 @@ Array [
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.c15 { .c16 {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.c18 { .c19 {
margin-right: 2rem; margin-right: 2rem;
} }
.c22 { .c23 {
margin-top: 1rem; margin-top: 1rem;
} }
.c14 { .c12 {
padding-right: 0;
padding-left: 0;
}
.c15 {
padding: 2rem; padding: 2rem;
} }
.c26 { .c27 {
padding-right: 1rem; padding-right: 1rem;
padding-left: 1rem; padding-left: 1rem;
} }
.c17 { .c18 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
@ -2346,7 +2351,7 @@ Array [
align-content: stretch; align-content: stretch;
} }
.c20 { .c21 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
@ -2453,32 +2458,33 @@ Array [
disabled={false} disabled={false}
name="card-header-meta" name="card-header-meta"
> >
<h4 <div
className="c12" className="c12"
>
<h4
className="c13"
> >
name name
</h4> </h4>
</div> </div>
</div> </div>
<div </div>
className="c13"
name="card-outlet"
>
<div <div
className="c14" className="c14"
name="card-outlet"
> >
<div <div
className="c15" className="c15"
> >
<p <div
className="c16" className="c16"
>
<p
className="c17"
> >
description description
</p> </p>
</div> </div>
<div
className="c17"
>
<div <div
className="c18" className="c18"
> >
@ -2489,10 +2495,13 @@ Array [
className="c20" className="c20"
> >
<div <div
className="c19" className="c21"
> >
<div <div
className="c21" className="c20"
>
<div
className="c22"
> >
<svg <svg
className="" className=""
@ -2515,10 +2524,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Private Private
</p> </p>
@ -2526,9 +2535,6 @@ Array [
</div> </div>
</div> </div>
</div> </div>
<div
className="c18"
>
<div <div
className="c19" className="c19"
> >
@ -2536,10 +2542,13 @@ Array [
className="c20" className="c20"
> >
<div <div
className="c19" className="c21"
> >
<div <div
className="c21" className="c20"
>
<div
className="c22"
> >
<svg <svg
height="16.2" height="16.2"
@ -2555,10 +2564,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Fabric network Fabric network
</p> </p>
@ -2566,31 +2575,165 @@ Array [
</div> </div>
</div> </div>
</div> </div>
</div> <div
className="c19"
>
<div
className="c20"
>
<div
className="c21"
>
<div
className="c20"
>
<div <div
className="c22" 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 <div
className="c23" className="c23"
>
<div
className="c24"
disabled={false} disabled={false}
name="card" name="card"
> >
<div <div
className="c24" className="c25"
disabled={false} disabled={false}
name="card-header" name="card-header"
onClick={[Function]} onClick={[Function]}
> >
<div <div
className="c25" className="c26"
disabled={false} disabled={false}
name="card-header-meta" name="card-header-meta"
> >
<div <div
className="c26" className="c27"
> >
<p <p
className="c16" className="c17"
> >
Network information Network information
</p> </p>
@ -2623,6 +2766,62 @@ Array [
</div> </div>
</div> </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> </div>
</div> </div>
@ -2644,12 +2843,12 @@ Array [
name="card" name="card"
> >
<div <div
className="c27" className="c28"
disabled={false} disabled={false}
name="card-header" name="card-header"
> >
<div <div
className="c28" className="c29"
disabled={false} disabled={false}
name="card-header-box" name="card-header-box"
> >
@ -2687,36 +2886,37 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c29" className="c30"
disabled={false} disabled={false}
name="card-header-meta" name="card-header-meta"
>
<div
className="c12"
> >
<h4 <h4
className="c30" className="c31"
> >
name2 name2
</h4> </h4>
</div> </div>
</div> </div>
<div </div>
className="c13"
name="card-outlet"
>
<div <div
className="c14" className="c14"
name="card-outlet"
> >
<div <div
className="c15" className="c15"
> >
<p <div
className="c16" className="c16"
>
<p
className="c17"
> >
description2 description2
</p> </p>
</div> </div>
<div
className="c17"
>
<div <div
className="c18" className="c18"
> >
@ -2727,10 +2927,13 @@ Array [
className="c20" className="c20"
> >
<div <div
className="c19" className="c21"
> >
<div <div
className="c21" className="c20"
>
<div
className="c22"
> >
<svg <svg
className="" className=""
@ -2753,10 +2956,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Private Private
</p> </p>
@ -2764,9 +2967,6 @@ Array [
</div> </div>
</div> </div>
</div> </div>
<div
className="c18"
>
<div <div
className="c19" className="c19"
> >
@ -2774,10 +2974,13 @@ Array [
className="c20" className="c20"
> >
<div <div
className="c19" className="c21"
> >
<div <div
className="c21" className="c20"
>
<div
className="c22"
> >
<svg <svg
height="13" height="13"
@ -2793,10 +2996,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Data center network Data center network
</p> </p>
@ -2887,6 +3090,11 @@ Array [
background-color: rgb(59,70,204); background-color: rgb(59,70,204);
border-radius: 0.25rem; border-radius: 0.25rem;
border: solid 0.0625rem rgb(45,56,132); 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 { .c1:focus {
@ -2915,6 +3123,23 @@ Array [
pointer-events: none; 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 { .c0 {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
@ -2924,7 +3149,7 @@ Array [
> >
<button <button
className="c1 c2 c3" className="c1 c2 c3"
disabled={false} disabled={true}
href="" href=""
onClick={undefined} onClick={undefined}
type="button" type="button"
@ -3105,9 +3330,9 @@ Array [
<h3 <h3
className="c0" className="c0"
> >
1 0
network network
s
added added
</h3>, </h3>,
.c7 { .c7 {

View File

@ -7776,35 +7776,6 @@ Array [
border-color: rgb(216,216,216); 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> <div>
<button <button
className="c0 c1 c2" className="c0 c1 c2"
@ -7814,14 +7785,6 @@ Array [
> >
Edit Edit
</button> </button>
<div
className="c3"
>
<div
className="c4 c5"
height="0.0625rem"
/>
</div>
</div>, </div>,
] ]
`; `;

View File

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

View File

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

View File

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

View File

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

View File

@ -10,8 +10,8 @@ import get from 'lodash.get';
import { MetadataIcon, Button, H3 } from 'joyent-ui-toolkit'; import { MetadataIcon, Button, H3 } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description'; import Description from '@components/description';
import KeyValue from '@components/instances/key-value'; import KeyValue from '@components/key-value';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-METADATA-ADD'; const FORM_NAME_CREATE = 'CREATE-INSTANCE-METADATA-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-METADATA-EDIT-${i}`; const FORM_NAME_EDIT = i => `CREATE-INSTANCE-METADATA-EDIT-${i}`;
@ -75,7 +75,8 @@ export const Metadata = ({
input="textarea" input="textarea"
type="metadata" type="metadata"
onToggleExpanded={() => onToggleExpanded={() =>
expanded ? handleToggleExpanded(index) : null} expanded ? handleToggleExpanded(index) : null
}
onCancel={() => handleCancelEdit(index)} onCancel={() => handleCancelEdit(index)}
onRemove={() => handleRemoveMetadata(index)} onRemove={() => handleRemoveMetadata(index)}
/> />
@ -138,7 +139,7 @@ export default compose(
set({ name: 'create-instance-metadata-proceeded', value: true }) set({ name: 'create-instance-metadata-proceeded', value: true })
); );
return history.push(`/instances/~create/user-script`); return history.push('/instances/~create/user-script');
}, },
handleEdit: () => { handleEdit: () => {
return history.push(`/instances/~create/metadata`); 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 { NameIcon, H3, Button } from 'joyent-ui-toolkit';
import Name from '@components/create-instance/name'; 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 Title from '@components/create-instance/title';
import GetInstance from '@graphql/get-instance-small.gql'; import GetInstance from '@graphql/get-instance-small.gql';
import GetRandomName from '@graphql/get-random-name.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 { 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 Title from '@components/create-instance/title';
import Network from '@components/create-instance/network';
import ListNetworks from '@graphql/list-networks.gql'; import ListNetworks from '@graphql/list-networks.gql';
const FORM_NAME = 'CREATE-INSTANCE-NETWORKS'; const FORM_NAME = 'CREATE-INSTANCE-NETWORKS';
export const Networks = ({ export const Networks = ({
networks = [], networks = [],
selected = [],
expanded = false, expanded = false,
proceeded = false, proceeded = false,
loading = false, loading = false,
setInfoExpanded, setInfoExpanded,
setMachinesExpanded,
handleNext, handleNext,
handleEdit handleEdit
}) => { }) => (
const selected = networks.filter(({ selected }) => selected);
return (
<Fragment> <Fragment>
<Title <Title
onClick={!expanded && !proceeded && handleEdit} onClick={!expanded && !proceeded && handleEdit}
@ -37,11 +36,10 @@ export const Networks = ({
</Title> </Title>
{expanded ? ( {expanded ? (
<Description> <Description>
Instances are automatically connected to a private fabric network, Instances are automatically connected to a private fabric network, which
which is the best choice for internal communication within your is the best choice for internal communication within your application.
application. Data center networks are the best choice for exposing Data center networks are the best choice for exposing your application
your application to the public internet (if the data center network is to the public internet (if the data center network is a public network).{' '}
a public network).{' '}
<a <a
target="__blank" target="__blank"
href="https://docs.joyent.com/public-cloud/network/sdn" href="https://docs.joyent.com/public-cloud/network/sdn"
@ -65,15 +63,19 @@ export const Networks = ({
{props => ( {props => (
<form> <form>
{networks.map( {networks.map(
({ id, selected, infoExpanded, ...network }) => ({ id, selected, infoExpanded, machinesExpanded, ...network }) =>
!expanded && !selected ? null : ( !expanded && !selected ? null : (
<Network <Network
key={id} key={id}
id={id} id={id}
selected={selected} selected={selected}
infoExpanded={infoExpanded} infoExpanded={infoExpanded}
machinesExpanded={machinesExpanded}
small={!expanded && selected} small={!expanded && selected}
onInfoClick={() => setInfoExpanded(id, !infoExpanded)} onInfoClick={() => setInfoExpanded(id, !infoExpanded)}
onMachinesClick={() =>
setMachinesExpanded(id, !machinesExpanded)
}
{...network} {...network}
/> />
) )
@ -84,11 +86,7 @@ export const Networks = ({
) : null} ) : null}
<Margin bottom={4}> <Margin bottom={4}>
{expanded ? ( {expanded ? (
<Button <Button type="button" disabled={!selected.length} onClick={handleNext}>
type="button"
disabled={!selected.length}
onClick={handleNext}
>
Next Next
</Button> </Button>
) : proceeded ? ( ) : proceeded ? (
@ -99,7 +97,6 @@ export const Networks = ({
</Margin> </Margin>
</Fragment> </Fragment>
); );
};
export default compose( export default compose(
graphql(ListNetworks, { graphql(ListNetworks, {
@ -119,9 +116,7 @@ export default compose(
const selected = get(form, `${FORM_NAME}.values`, {}); const selected = get(form, `${FORM_NAME}.values`, {});
const empty = id => !Object.keys(selected).includes(id); const empty = id => !Object.keys(selected).includes(id);
return { const _networks = networks
proceeded: get(values, 'create-instance-networks-proceeded', false),
networks: networks
.map(({ id, name, ...network }) => ({ .map(({ id, name, ...network }) => ({
...network, ...network,
name, name,
@ -131,12 +126,22 @@ export default compose(
: Boolean(selected[id]), : Boolean(selected[id]),
infoExpanded: get( infoExpanded: get(
values, values,
`create-instance-networks-${id}-expanded`, `create-instance-networks-${id}-info-expanded`,
false
),
machinesExpanded: get(
values,
`create-instance-networks-${id}-machines-expanded`,
false false
), ),
id 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 }) => ({ (dispatch, { history }) => ({
@ -153,7 +158,15 @@ export default compose(
setInfoExpanded: (id, expanded) => { setInfoExpanded: (id, expanded) => {
return dispatch( return dispatch(
set({ 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 value: expanded
}) })
); );

View File

@ -11,17 +11,18 @@ import constantCase from 'constant-case';
import { reset } from 'redux-form'; import { reset } from 'redux-form';
import { PackageIcon, StatusLoader } from 'joyent-ui-toolkit'; import { PackageIcon, StatusLoader } from 'joyent-ui-toolkit';
import { import {
Filters, Filters,
Packages, Packages,
Package, Package,
Overview Overview
} from '@components/create-instance/package'; } 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 FORM_NAME = 'create-instance-package';
const FILTERS = 'create-instance-package-filters'; 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 { TagsIcon, Button, H3, TagList } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Tag from '@components/instances/tags'; import KeyValue from '@components/key-value';
import KeyValue from '@components/instances/key-value'; import Description from '@components/description';
import Description from '@components/create-instance/description'; import Tag from '@components/tags';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-TAGS-ADD'; const FORM_NAME_CREATE = 'CREATE-INSTANCE-TAGS-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-TAGS-EDIT-${i}`; 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 { Margin } from 'styled-components-spacing';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import get from 'lodash.get'; 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 KeyValue from '@components/key-value';
import Description from '@components/create-instance/description'; import Description from '@components/description';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
const FORM_NAME = 'create-instance-user-script'; const FORM_NAME = 'create-instance-user-script';
@ -84,9 +83,6 @@ export const UserScript = ({
<Button type="button" onClick={handleEdit} secondary> <Button type="button" onClick={handleEdit} secondary>
Edit Edit
</Button> </Button>
<Margin top={4}>
<Divider height={remcalc(1)} />
</Margin>
</Fragment> </Fragment>
) : null} ) : null}
</div> </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 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 { export default () => <p>firewalls</p>;
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'; // import PropTypes from 'prop-types';
// import forceArray from 'force-array';
const Firewall = ({ // import { compose, graphql } from 'react-apollo';
// eslint-disable-next-line camelcase // import find from 'lodash.find';
firewallEnabled = false, // import get from 'lodash.get';
firewallRules = [], //
loading, // import {
error // ViewContainer,
}) => { // StatusLoader,
const values = forceArray(firewallRules); // Message,
const _loading = !(loading && !values.length) ? null : <StatusLoader />; // MessageDescription,
// MessageTitle,
const _firewall = // Table,
_loading && !values.length ? null : ( // TableThead,
<Table> // TableTr,
<TableThead> // TableTh,
<TableTr> // TableTbody,
<TableTh left bottom> // P
<P>Rule</P> // } from 'joyent-ui-toolkit';
</TableTh> //
<TableTh xs="63" center bottom> // import GetFirewallRules from '@graphql/list-firewall-rules.gql';
<P>Global</P> // import { FirewallRule as InstanceFirewallRule } from '@components/instances';
</TableTh> //
<TableTh xs="75" center bottom> // const Firewall = ({
<P>Enabled</P> // // eslint-disable-next-line camelcase
</TableTh> // firewallEnabled = false,
</TableTr> // firewallRules = [],
</TableThead> // loading,
<TableTbody> // error
{values.map(network => ( // }) => {
<InstanceFirewallRule key={network.id} {...network} /> // const values = forceArray(firewallRules);
))} // const _loading = !(loading && !values.length) ? null : <StatusLoader />;
</TableTbody> //
</Table> // const _firewall =
); // _loading && !values.length ? null : (
// <Table>
const _error = // <TableThead>
error && !values.length && !_loading ? ( // <TableTr>
<Message error> // <TableTh left bottom>
<MessageTitle>Ooops!</MessageTitle> // <P>Rule</P>
<MessageDescription> // </TableTh>
An error occurred while loading your instance firewall rules // <TableTh xs="63" center bottom>
</MessageDescription> // <P>Global</P>
</Message> // </TableTh>
) : null; // <TableTh xs="75" center bottom>
// <P>Enabled</P>
return ( // </TableTh>
<ViewContainer center={Boolean(_loading)} main> // </TableTr>
{_loading} // </TableThead>
{_error} // <TableTbody>
{_firewall} // {values.map(network => (
</ViewContainer> // <InstanceFirewallRule key={network.id} {...network} />
); // ))}
}; // </TableTbody>
// </Table>
Firewall.propTypes = { // );
loading: PropTypes.bool //
}; // const _error =
// error && !values.length && !_loading ? (
export default compose( // <Message error>
graphql(GetFirewallRules, { // <MessageTitle>Ooops!</MessageTitle>
options: ({ match }) => ({ // <MessageDescription>
pollInterval: 1000, // An error occurred while loading your instance firewall rules
variables: { // </MessageDescription>
name: get(match, 'params.instance') // </Message>
} // ) : null;
}), //
props: ({ data: { loading, error, variables, ...rest } }) => { // return (
const machine = find(get(rest, 'machines', []), ['name', variables.name]); // <ViewContainer center={Boolean(_loading)} main>
// eslint-disable-next-line camelcase // {_loading}
const firewallEnabled = get(machine, 'firewall_enabled', false); // {_error}
const firewallRules = get(machine, 'firewall_rules', []); // {_firewall}
// </ViewContainer>
// eslint-disable-next-line camelcase // );
return { firewallEnabled, firewallRules, loading, error }; // };
} //
}) // Firewall.propTypes = {
)(Firewall); // 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 React from 'react';
import PropTypes from 'prop-types';
import forceArray from 'force-array';
import { compose, graphql } from 'react-apollo'; 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 find from 'lodash.find';
import sortBy from 'lodash.sortby';
import get from 'lodash.get'; import get from 'lodash.get';
import { import {
ViewContainer, ViewContainer,
StatusLoader, StatusLoader,
Message, Message,
MessageDescription,
MessageTitle, MessageTitle,
Table, MessageDescription,
TableThead, H3
TableTr,
TableTh,
TableTbody,
P
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import GetNetworks from '@graphql/list-networks.gql'; import Network from '@components/network';
import { Network as InstanceNetwork } from '@components/instances'; import Description from '@components/description';
import GetNetworks from '@graphql/list-instance-networks.gql';
const Networks = ({ networks = [], loading, error }) => { export const Networks = ({
const values = forceArray(networks); networks = [],
const _loading = !(loading && !values.length) ? null : <StatusLoader />; loading = false,
error = null,
const _networks = setMachinesExpanded,
_loading && !values.length ? null : ( setInfoExpanded
<Table> }) => (
<TableThead> <ViewContainer main>
<TableTr> <Margin bottom={1}>
<TableTh left bottom> <Description>
<P>Name</P> Use predefined or customized fabric networks which can be public-facing
</TableTh> or private. All fabrics are isolated from other customers giving you
<TableTh xs="90" left bottom> complete control over the network environment. Read more on fabrics.{' '}
<P>Gateway</P> <a
</TableTh> target="__blank"
<TableTh xs="90" left bottom> href="https://docs.joyent.com/public-cloud/network/sdn"
<P>Subnet</P> >
</TableTh> Read more
<TableTh xs="90" left bottom> </a>
<P>Resolvers</P> </Description>
</TableTh> </Margin>
</TableTr> <H3>Networks attached to this instance</H3>
</TableThead> {loading ? <StatusLoader /> : null}
<TableTbody> {!loading && error ? (
{values.map(network => (
<InstanceNetwork key={network.id} {...network} />
))}
</TableTbody>
</Table>
);
const _error =
error && !values.length && !_loading ? (
<Message error> <Message error>
<MessageTitle>Ooops!</MessageTitle> <MessageTitle>Ooops!</MessageTitle>
<MessageDescription> <MessageDescription>
An error occurred while loading your instance networks An error occurred while loading your networks
</MessageDescription> </MessageDescription>
</Message> </Message>
) : null; ) : null}
{!loading &&
return ( networks.map(({ id, infoExpanded, machinesExpanded, ...network }) => (
<ViewContainer center={Boolean(_loading)} main> <Network
{_loading} {...network}
{_error} key={id}
{_networks} id={id}
infoExpanded={infoExpanded}
machinesExpanded={machinesExpanded}
onInfoClick={() => setInfoExpanded(id, !infoExpanded)}
onMachinesClick={() => setMachinesExpanded(id, !machinesExpanded)}
selected
readOnly
/>
))}
</ViewContainer> </ViewContainer>
); );
};
Networks.propTypes = {
loading: PropTypes.bool
};
export default compose( export default compose(
graphql(GetNetworks, { graphql(GetNetworks, {
options: ({ match }) => ({ options: ({ match }) => ({
pollInterval: 1000,
variables: { variables: {
name: get(match, 'params.instance') name: get(match, 'params.instance')
} }
}), }),
props: ({ data: { loading, error, variables, ...rest } }) => ({ props: ({ data }) => {
networks: get( const { machines, loading, error, variables } = data;
find(get(rest, 'machines', []), ['name', variables.name]), const { name } = variables;
'networks',
[] const instance = find(forceArray(machines), ['name', name]);
), const values = get(instance, 'networks', []);
const networks = sortBy(values, 'public').reverse();
return {
networks,
instance,
loading, loading,
error 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); )(Networks);

View File

@ -18,20 +18,20 @@ import {
StatusLoader StatusLoader
} from 'joyent-ui-toolkit'; } 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 { import {
default as SnapshotsList, default as SnapshotsList,
AddForm as SnapshotAddForm AddForm as SnapshotAddForm
} from '@components/instances/snapshots'; } 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 MENU_FORM_NAME = 'snapshot-list-menu';
const TABLE_FORM_NAME = 'snapshot-list-table'; const TABLE_FORM_NAME = 'snapshot-list-table';
const CREATE_FORM_NAME = 'create-snapshot-form'; const CREATE_FORM_NAME = 'create-snapshot-form';

View File

@ -23,14 +23,14 @@ import {
default as Tag, default as Tag,
AddForm as TagsAddForm, AddForm as TagsAddForm,
EditForm as TagsEditForm EditForm as TagsEditForm
} from '@components/instances/tags'; } from '@components/tags';
import ToolbarForm from '@components/instances/toolbar';
import GetTags from '@graphql/list-tags.gql'; import GetTags from '@graphql/list-tags.gql';
import UpdateTags from '@graphql/update-tags.gql'; import UpdateTags from '@graphql/update-tags.gql';
import DeleteTag from '@graphql/delete-tag.gql'; import DeleteTag from '@graphql/delete-tag.gql';
import Index from '@state/gen-index'; import Index from '@state/gen-index';
import parseError from '@state/parse-error'; import parseError from '@state/parse-error';
import ToolbarForm from '@components/instances/toolbar';
const MENU_FORM_NAME = 'instance-tags-list-menu'; const MENU_FORM_NAME = 'instance-tags-list-menu';
const ADD_FORM_NAME = 'instance-tags-add-new'; 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 gateway
resolvers resolvers
internet_nat internet_nat
machines {
id
name
}
} }
} }

View File

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