refactor(instances): refactor create instance flow

fixes #1354
This commit is contained in:
johnytiago 2018-04-12 10:53:00 +01:00 committed by Sérgio Ramos
parent 316773d9b7
commit 32d5cdc293
683 changed files with 25062 additions and 52331 deletions

1
.gitignore vendored
View File

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

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

@ -0,0 +1 @@
{}

View File

@ -5,7 +5,7 @@ const RenderReact = require('hapi-render-react');
const Intercept = require('apr-intercept'); const Intercept = require('apr-intercept');
const Fs = require('mz/fs'); const Fs = require('mz/fs');
const { NAMESPACE = 'images' } = process.env; const { NAMESPACE = 'images', NODE_ENV = 'development' } = process.env;
exports.register = async server => { exports.register = async server => {
let manifest = {}; let manifest = {};

View File

@ -15,7 +15,7 @@ import {
PopoverTarget, PopoverTarget,
Popover, Popover,
PopoverItem, PopoverItem,
PopoverDivider, PopoverDivider as BasePopoverDivider,
PopoverContainer, PopoverContainer,
Radio, Radio,
FormLabel, FormLabel,
@ -59,10 +59,6 @@ const Max = styled(Flex)`
height: 100%; height: 100%;
`; `;
const DividerContainer = styled(Margin)`
height: 100%;
`;
const Actions = styled(Flex)` const Actions = styled(Flex)`
width: ${remcalc(48)}; width: ${remcalc(48)};
height: ${remcalc(48)}; height: ${remcalc(48)};
@ -71,9 +67,14 @@ const Actions = styled(Flex)`
const ActionsWrapper = styled(Flex)` const ActionsWrapper = styled(Flex)`
height: 100%; height: 100%;
width: 100%;
border-left: ${remcalc(1)} solid ${props => props.theme.grey}; border-left: ${remcalc(1)} solid ${props => props.theme.grey};
`; `;
const PopoverDivider = styled(BasePopoverDivider)`
width: 100%;
`;
export const Image = ({ export const Image = ({
name, name,
id, id,
@ -113,10 +114,8 @@ export const Image = ({
<Max justifyBetween> <Max justifyBetween>
<Max alignCenter> <Max alignCenter>
<Flex>{version}</Flex> <Flex>{version}</Flex>
<DividerContainer left={2}> <Divider vertical />
<Divider width={remcalc(1)} height="100%" /> <Type>{ImageType[type]}</Type>
</DividerContainer>
<Type left={2}>{ImageType[type]}</Type>
</Max> </Max>
</Max> </Max>
</Content> </Content>
@ -127,22 +126,26 @@ export const Image = ({
<ActionsIcon /> <ActionsIcon />
</ActionsWrapper> </ActionsWrapper>
</PopoverTarget> </PopoverTarget>
<Popover placement="bottom"> <Popover noPadding placement="bottom">
<PopoverItem disabled={false} onClick={onCreateInstance}> <Padding horizontal={3} vertical={2}>
<ItemAnchor <PopoverItem disabled={false} onClick={onCreateInstance}>
href={`${ <ItemAnchor
GLOBAL.origin href={`${
}/instances/~create/?image=${name}`} GLOBAL.origin
target="__blank" }/instances/~create/?image=${name}`}
rel="noopener noreferrer" target="__blank"
> rel="noopener noreferrer"
Create Instance >
</ItemAnchor> Create Instance
</PopoverItem> </ItemAnchor>
</PopoverItem>
</Padding>
<PopoverDivider /> <PopoverDivider />
<PopoverItem disabled={removing} onClick={onRemove}> <Padding horizontal={3} vertical={2}>
Remove <PopoverItem disabled={removing} onClick={onRemove}>
</PopoverItem> Remove
</PopoverItem>
</Padding>
</Popover> </Popover>
</Actions> </Actions>
</PopoverContainer> </PopoverContainer>
@ -157,9 +160,9 @@ export const Image = ({
export const Filters = ({ selected }) => ( export const Filters = ({ selected }) => (
<Fragment> <Fragment>
<FormGroup name="image-type" value="all" field={Field} type="radio"> <FormGroup name="image-type" value="all" field={Field} type="radio">
<Radio noMargin> <Radio>
<Flex alignCenter> <Flex alignCenter>
<Margin right={2}> <Margin horizontal={2}>
<FormLabel big normal={selected !== 'all'}> <FormLabel big normal={selected !== 'all'}>
All All
</FormLabel> </FormLabel>
@ -175,7 +178,7 @@ export const Filters = ({ selected }) => (
> >
<Radio noMargin> <Radio noMargin>
<Flex alignCenter> <Flex alignCenter>
<Margin right={2}> <Margin horizontal={2}>
<FormLabel big normal={selected !== 'hardware-virtual-machine'}> <FormLabel big normal={selected !== 'hardware-virtual-machine'}>
Virtual machines Virtual machines
</FormLabel> </FormLabel>
@ -191,7 +194,7 @@ export const Filters = ({ selected }) => (
> >
<Radio noMargin> <Radio noMargin>
<Flex alignCenter> <Flex alignCenter>
<Margin right={2}> <Margin horizontal={2}>
<FormLabel big normal={selected !== 'infrastructure-container'}> <FormLabel big normal={selected !== 'infrastructure-container'}>
Infrastructure container Infrastructure container
</FormLabel> </FormLabel>

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import forceArray from 'force-array';
import { Margin } from 'styled-components-spacing'; import { Margin } from 'styled-components-spacing';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import forceArray from 'force-array';
import { import {
SectionList, SectionList,

View File

@ -104,7 +104,7 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
<Col xs={12} sm={12} md={9}> <Col xs={12} sm={12} md={9}>
<Card> <Card>
<CardOutlet> <CardOutlet>
<Padding all={4}> <Padding all={5}>
<Meta {...image} /> <Meta {...image} />
<Row between="xs"> <Row between="xs">
<Col xs={9}> <Col xs={9}>
@ -124,7 +124,6 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
bold bold
icon icon
> >
<DuplicateIcon light />
<span>Create Instance</span> <span>Create Instance</span>
</Button> </Button>
</Medium> </Medium>
@ -145,7 +144,9 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
error error
right right
> >
<DeleteIcon fill={theme.red} /> <Margin right="1">
<DeleteIcon fill={theme.red} />
</Margin>
<span>Remove</span> <span>Remove</span>
</Button> </Button>
</Medium> </Medium>
@ -157,17 +158,23 @@ export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
<Margin bottom="2"> <Margin bottom="2">
<P>{image.description}</P> <P>{image.description}</P>
</Margin> </Margin>
<CopiableField text={(image.id || '').split('-')[0]} label="ID" /> <Margin bottom={3}>
<CopiableField text={image.id} label="UUID" /> <CopiableField text={(image.id || '').split('-')[0]} label="ID" />
</Margin>
<Margin bottom={3}>
<CopiableField text={image.id} label="UUID" />
</Margin>
<Row> <Row>
<Col xs={12} md={7}> <Col xs={12} md={7}>
<FormLabel>Operating system</FormLabel> <Margin bottom={3}>
<Input <FormLabel>Operating system</FormLabel>
monospace <Input
onBlur={null} monospace
fluid onBlur={null}
value={titleCase(image.os)} fluid
/> value={titleCase(image.os)}
/>
</Margin>
</Col> </Col>
</Row> </Row>
</Padding> </Padding>

View File

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

View File

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

View File

@ -66,7 +66,7 @@ export const Tags = ({
) : null} ) : null}
{proceeded || expanded ? ( {proceeded || expanded ? (
<Fragment> <Fragment>
<Margin bottom={4}> <Margin bottom={5}>
<H3> <H3>
{tags.length} Tag{tags.length === 1 ? '' : 's'} {tags.length} Tag{tags.length === 1 ? '' : 's'}
</H3> </H3>

View File

@ -54,7 +54,7 @@ const Create = ({
</Margin> </Margin>
) : null} ) : null}
{!loading && !loadingError ? ( {!loading && !loadingError ? (
<Margin top={4} bottom={4}> <Margin top={4} bottom={5}>
<H2>Create Image</H2> <H2>Create Image</H2>
</Margin> </Margin>
) : null} ) : null}

View File

@ -11,6 +11,7 @@ import { set } from 'react-redux-values';
import Fuse from 'fuse.js'; import Fuse from 'fuse.js';
import { import {
H3,
ViewContainer, ViewContainer,
Divider, Divider,
StatusLoader, StatusLoader,
@ -41,12 +42,17 @@ export const List = ({
handleRemove handleRemove
}) => ( }) => (
<ViewContainer main> <ViewContainer main>
<Divider height={remcalc(30)} transparent /> <Margin top={5}>
<ReduxForm form={LIST_TOOLBAR_FORM}> <H3>Images</H3>
{props => <ToolbarForm {...props} actionable={!loading} />} </Margin>
</ReduxForm> <Margin top={4}>
<Divider height={remcalc(1)} /> <ReduxForm form={LIST_TOOLBAR_FORM}>
<Divider height={remcalc(24)} transparent /> {props => <ToolbarForm {...props} actionable={!loading} />}
</ReduxForm>
</Margin>
<Margin vertical={4}>
<Divider />
</Margin>
{loading && !images.length ? ( {loading && !images.length ? (
<Fragment> <Fragment>
<Divider height={remcalc(30)} transparent /> <Divider height={remcalc(30)} transparent />
@ -54,7 +60,7 @@ export const List = ({
</Fragment> </Fragment>
) : null} ) : null}
{error && !images.length && !loading ? ( {error && !images.length && !loading ? (
<Margin bottom={4}> <Margin bottom={5}>
<Message error> <Message error>
<MessageTitle>Ooops!</MessageTitle> <MessageTitle>Ooops!</MessageTitle>
<MessageDescription> <MessageDescription>

View File

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

View File

@ -56,12 +56,14 @@ export const Tags = ({
onActionClick={handleToggleAddOpen} onActionClick={handleToggleAddOpen}
action action
/> />
<Divider height={remcalc(1)} /> <Margin vertical="4">
<Divider height={remcalc(1)} />
</Margin>
</Margin> </Margin>
)} )}
</ReduxForm> </ReduxForm>
{error && !loading && !tags.length ? ( {error && !loading && !tags.length ? (
<Margin bottom={4}> <Margin bottom={5}>
<Message error> <Message error>
<MessageTitle>Ooops!</MessageTitle> <MessageTitle>Ooops!</MessageTitle>
<MessageDescription> <MessageDescription>
@ -71,7 +73,7 @@ export const Tags = ({
</Margin> </Margin>
) : null} ) : null}
{mutationError ? ( {mutationError ? (
<Margin bottom={4}> <Margin bottom={5}>
<Message error> <Message error>
<MessageTitle>Ooops!</MessageTitle> <MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription> <MessageDescription>{mutationError}</MessageDescription>
@ -80,13 +82,13 @@ export const Tags = ({
) : null} ) : null}
<ReduxForm <ReduxForm
form={TAGS_ADD_FORM} form={TAGS_ADD_FORM}
asyncValidate={handleAsyncValidate}
shouldAsyncValidate={shouldAsyncValidate} shouldAsyncValidate={shouldAsyncValidate}
asyncValidate={handleAsyncValidate}
onSubmit={handleAddTag} onSubmit={handleAddTag}
> >
{props => {props =>
addOpen ? ( addOpen ? (
<Margin bottom={4}> <Margin bottom={5}>
<AddForm <AddForm
{...props} {...props}
onToggleExpanded={() => handleToggleAddOpen(!addOpen)} onToggleExpanded={() => handleToggleAddOpen(!addOpen)}
@ -97,7 +99,7 @@ export const Tags = ({
} }
</ReduxForm> </ReduxForm>
{!loading ? ( {!loading ? (
<Margin bottom={4}> <Margin bottom={5}>
<H3> <H3>
{tags.length} tag{tags.length === 1 ? '' : 's'} {tags.length} tag{tags.length === 1 ? '' : 's'}
</H3> </H3>
@ -105,9 +107,10 @@ export const Tags = ({
) : null} ) : null}
{loading && !tags.length ? <StatusLoader /> : null} {loading && !tags.length ? <StatusLoader /> : null}
<TagList> <TagList>
{tags.map(({ name, value }) => ( {tags.map(({ id, name, value }) => (
<Tag <Tag
key={value} key={id}
id={id}
name={name} name={name}
value={value} value={value}
onRemoveClick={!mutating && (() => handleRemoveTag(name))} onRemoveClick={!mutating && (() => handleRemoveTag(name))}

View File

@ -10,12 +10,11 @@ module.exports = ({
<head> <head>
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" /> <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="theme-color" content="#1E313B" />
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/> />
<meta name="theme-color" content="#1E313B" />
{head} {head}
</head> </head>

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { HelmetProvider } from 'react-helmet-async';
import { ThemeProvider } from 'styled-components'; import { ThemeProvider } from 'styled-components';
import { Provider as ReduxProvider } from 'react-redux'; import { Provider as ReduxProvider } from 'react-redux';
import { ApolloProvider } from 'react-apollo'; import { ApolloProvider } from 'react-apollo';
@ -21,7 +22,9 @@ ReactDOM.hydrate(
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<ReduxProvider store={createStore()}> <ReduxProvider store={createStore()}>
<BrowserRouter> <BrowserRouter>
<App /> <HelmetProvider context={{}}>
<App />
</HelmetProvider>
</BrowserRouter> </BrowserRouter>
</ReduxProvider> </ReduxProvider>
</ThemeProvider> </ThemeProvider>

View File

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

View File

@ -14,14 +14,16 @@ const validateField = async (field, value) => {
const validateSchema = async (schema, value) => { const validateSchema = async (schema, value) => {
const errors = await reduce( const errors = await reduce(
keys(schema), keys(schema),
async (errors, name) => async (errors, name) => {
assign(errors, { const msg = await validateField(schema[name], value[name]);
[name]: await validateField(schema[name], value[name]) return !msg ? errors : assign(errors, { [name]: msg });
}), },
{} {}
); );
throw errors; if (keys(errors).length) {
throw errors;
}
}; };
/*****************************************************************************/ /*****************************************************************************/
@ -61,7 +63,10 @@ const Schemas = {
/*****************************************************************************/ /*****************************************************************************/
export const addTag = tag => validateSchema(Schemas.tag, tag); export const addTag = tag => {
console.log(tag);
validateSchema(Schemas.tag, tag);
};
export const instanceName = ({ name }) => export const instanceName = ({ name }) =>
!name ? null : validateSchema(Schemas.instanceName, { name }); !name ? null : validateSchema(Schemas.instanceName, { name });

View File

@ -38,6 +38,7 @@
"hapi-render-react-joyent-document": "^7.1.0", "hapi-render-react-joyent-document": "^7.1.0",
"inert": "^5.1.0", "inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0", "joyent-logo-assets": "^1.1.0",
"joyent-ui-resource-step": "^1.0.0",
"joyent-manifest-editor": "^1.4.0", "joyent-manifest-editor": "^1.4.0",
"joyent-react-styled-flexboxgrid": "^2.2.3", "joyent-react-styled-flexboxgrid": "^2.2.3",
"joyent-ui-toolkit": "^6.0.0", "joyent-ui-toolkit": "^6.0.0",

View File

@ -1,6 +1,5 @@
const get = require('lodash.get'); const get = require('lodash.get');
const Document = require('hapi-render-react-joyent-document'); const Document = require('hapi-render-react-joyent-document');
const path = require('path');
const url = require('url'); const url = require('url');
const { theme } = require('joyent-ui-toolkit'); const { theme } = require('joyent-ui-toolkit');

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