From c765f2e8fe782af7edad4860e2bcb6b296d67366 Mon Sep 17 00:00:00 2001 From: Sara Vieira Date: Wed, 22 Jan 2020 15:24:10 +0100 Subject: [PATCH] Deployment sidebar (#3368) * add netlify * fix padding * fix ts * fuix ts * fix ts? * add zeit * place defaultOpen: true in the right place * update message --- .vscode/settings.json | 6 +- .../pages/Sandbox/Editor/Workspace/index.tsx | 3 +- .../Workspace/screens/Deployment/Netlify.tsx | 113 ++++++++++++++++++ .../Workspace/screens/Deployment/Zeit.tsx | 90 ++++++++++++++ .../Workspace/screens/Deployment/elements.ts | 49 ++++++++ .../Workspace/screens/Deployment/icons.tsx | 70 +++++++++++ .../Workspace/screens/Deployment/index.tsx | 68 +++++++++++ .../Workspace/screens/ProjectInfo/Title.tsx | 2 - .../codesandbox-api/src/dispatcher/index.ts | 1 + .../src/components/Button/index.tsx | 2 + .../src/components/Element/index.tsx | 18 +++ .../components/src/components/Input/index.tsx | 10 +- .../components/src/components/List/index.tsx | 21 ++-- .../src/components/Select/index.tsx | 10 +- packages/sandbox-hooks/console/index.js | 1 + 15 files changed, 448 insertions(+), 16 deletions(-) create mode 100644 packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx create mode 100644 packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx create mode 100644 packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts create mode 100644 packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx create mode 100644 packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 9d50eeedb5a..399806e1633 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,9 @@ "javascriptreact", { "language": "typescript", "autoFix": true }, { "language": "typescriptreact", "autoFix": true } - ] + ], + "files.watcherExclude": { + "**/node_modules": true, + "**/node_modules/**": false + } } diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx index 4a873cee38f..cf0f9aa6508 100644 --- a/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx @@ -25,6 +25,7 @@ import { More } from './items/More'; import { NotOwnedSandboxInfo } from './items/NotOwnedSandboxInfo'; import { ProjectInfo } from './items/ProjectInfo'; import { ProjectInfo as ProjectInfoNew } from './screens/ProjectInfo'; +import { Deployment as DeploymentNew } from './screens/Deployment/index'; import { Server } from './items/Server'; import { SSEDownNotice } from './SSEDownNotice'; @@ -37,7 +38,7 @@ const workspaceTabs = { 'project-summary': NotOwnedSandboxInfo, files: FilesItem, github: GitHub, - deploy: Deployment, + deploy: REDESIGNED_SIDEBAR ? DeploymentNew : Deployment, config: REDESIGNED_SIDEBAR ? ConfigurationFilesNew : ConfigurationFiles, live: Live, server: Server, diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx new file mode 100644 index 00000000000..8c0ba4e2a4b --- /dev/null +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx @@ -0,0 +1,113 @@ +import React, { useEffect } from 'react'; +import getTemplate from '@codesandbox/common/lib/templates'; +import track from '@codesandbox/common/lib/utils/analytics'; +import { useOvermind } from 'app/overmind'; + +import { + Element, + ListAction, + Text, + Stack, + List, + ListItem, + Button, + Integration, +} from '@codesandbox/components'; +import { css } from '@styled-system/css'; +import { NetlifyIcon, FileIcon, VisitIcon, FlagIcon } from './icons'; + +export const Netlify = () => { + const { + actions: { + modalOpened, + deployment: { deployWithNetlify, getNetlifyDeploys }, + }, + state: { + deployment: { + deploying, + netlifySite, + building, + netlifyLogs, + netlifyClaimUrl, + }, + editor: { currentSandbox }, + }, + } = useOvermind(); + + useEffect(() => { + getNetlifyDeploys(); + }, [getNetlifyDeploys]); + + const template = getTemplate(currentSandbox.template); + + return ( + template.netlify !== false && ( + <> + + + Enables + Deployments + + + + {netlifySite ? ( + + + {netlifySite.name} + + {building && !netlifyLogs && ( + + Building + + )} + {netlifySite.url ? ( + window.open(netlifySite.url, '_blank')} + > + + + {' '} + Visit Site + + ) : null} + {netlifySite.url ? ( + window.open(netlifyClaimUrl, '_blank')} + > + + + {' '} + Claim Site + + ) : null} + {netlifyLogs ? ( + modalOpened({ modal: 'netlifyLogs' })} + > + + + {' '} + View Logs + + ) : null} + + ) : null} + + + + ) + ); +}; diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx new file mode 100644 index 00000000000..d085867b664 --- /dev/null +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { + Element, + Text, + Stack, + Button, + Integration, + ListAction, + ListItem, + List, +} from '@codesandbox/components'; +import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; +import { css } from '@styled-system/css'; +import { useOvermind } from 'app/overmind'; +import { ZeitIcon, VisitIcon, TrashIcon } from './icons'; +import { State } from './elements'; + +export const Zeit = () => { + const { + actions: { modalOpened, deployment }, + state: { + deployment: { deploying, sandboxDeploys, deploysBeingDeleted }, + user: { integrations }, + }, + } = useOvermind(); + const { deploySandboxClicked, setDeploymentToDelete } = deployment; + return ( + + + + Enables + Deployments + + + + {sandboxDeploys && + sandboxDeploys.map(deploy => { + const disabled = deploysBeingDeleted + ? deploysBeingDeleted.includes(deploy.uid) + : deployment[`${deploy.uid}Deleting`]; + return ( + + + {deploy.name} + + + + {deploy.state.toString().toLowerCase()} + + + {`(${distanceInWordsToNow(deploy.created)} ago)`} + + + + window.open(`https://${deploy.url}`, '_blank') + } + > + + + {' '} + Visit Site + + { + setDeploymentToDelete({ id: deploy.uid }); + modalOpened({ modal: 'deleteDeployment' }); + }} + > + + + {' '} + Delete + + + ); + })} + + + + ); +}; diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts new file mode 100644 index 00000000000..9c61fd27f45 --- /dev/null +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts @@ -0,0 +1,49 @@ +import { ZeitDeploymentState } from '@codesandbox/common/lib/types'; +import styled, { css } from 'styled-components'; +import { Text } from '@codesandbox/components'; + +const mapColorToState = (state: ZeitDeploymentState, theme: any) => { + const STARTING = [ + ZeitDeploymentState.BUILDING, + ZeitDeploymentState.DEPLOYING, + ZeitDeploymentState.INITIALIZING, + ]; + const ERROR = [ + ZeitDeploymentState.BUILD_ERROR, + ZeitDeploymentState.DEPLOYMENT_ERROR, + ZeitDeploymentState.ERROR, + ]; + const STARTED = [ZeitDeploymentState.BOOTED, ZeitDeploymentState.READY]; + + if (STARTING.includes(state)) { + return '#FCCB7E'; + } + if (ERROR.includes(state)) { + return theme.red; + } + if (STARTED.includes(state)) { + return theme.green; + } + if (state === ZeitDeploymentState.FROZEN) { + return theme.blue; + } + + return theme.gray; +}; + +export const State = styled(Text)<{ state: ZeitDeploymentState }>` + ${({ state, theme }) => css` + display: flex; + align-items: center; + text-transform: capitalize; + &:before { + content: ''; + display: block; + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 0.5rem; + background: ${mapColorToState(state, theme)}; + } + `}; +`; diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx new file mode 100644 index 00000000000..c9e225f44fc --- /dev/null +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx @@ -0,0 +1,70 @@ +import React from 'react'; + +export const NetlifyIcon = props => ( + + + +); + +export const ZeitIcon = props => ( + + + +); + +export const FileIcon = props => ( + + + + + + + +); + +export const VisitIcon = props => ( + + + +); + +export const FlagIcon = props => ( + + + +); + +export const TrashIcon = props => ( + + + +); diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx new file mode 100644 index 00000000000..0bfddcd7179 --- /dev/null +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx @@ -0,0 +1,68 @@ +import React, { FunctionComponent, useEffect } from 'react'; + +import { useOvermind } from 'app/overmind'; + +import { Element, Collapsible, Text } from '@codesandbox/components'; + +import { Netlify } from './Netlify'; +import { Zeit } from './Zeit'; + +import { More } from '../../items/More'; + +export const Deployment: FunctionComponent = () => { + const { + actions: { + deployment: { getDeploys }, + }, + state: { + editor: { currentSandbox }, + isLoggedIn, + }, + } = useOvermind(); + const showPlaceholder = !(currentSandbox.owned && isLoggedIn); + + useEffect(() => { + if (!showPlaceholder) { + getDeploys(); + } + }, [getDeploys, showPlaceholder]); + + if (showPlaceholder) { + const message = isLoggedIn ? ( + <> + + You need to own this sandbox to deploy this sandbox to Netlify or + ZEIT. + + Fork this sandbox to make a deploy! + + ) : ( + + You need to be signed in to deploy this sandbox to Netlify or ZEIT. + + ); + + return ( + + + + + + ); + } + + return ( + + + + You can deploy a production version of your sandbox using one our + supported providers. + + + + + + + + ); +}; diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx index 9d0d5bb4e2d..dcba4186be9 100644 --- a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx +++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx @@ -72,9 +72,7 @@ export const Title = ({ editable }) => { onKeyUp={onKeyUp} placeholder="Title" ref={(el: any) => el && el.focus()} - // @ts-ignore type="text" - // @ts-ignore value={title} /> diff --git a/packages/codesandbox-api/src/dispatcher/index.ts b/packages/codesandbox-api/src/dispatcher/index.ts index 10954b533da..d7c315a5b04 100644 --- a/packages/codesandbox-api/src/dispatcher/index.ts +++ b/packages/codesandbox-api/src/dispatcher/index.ts @@ -95,6 +95,7 @@ export function listen(callback: Callback): () => void { } export function notifyListeners(data: object, source?: MessageEvent['source']) { + // eslint-disable-next-line no-shadow Object.keys(listeners).forEach(listenerId => { if (listeners[listenerId]) { listeners[listenerId](data, source); diff --git a/packages/components/src/components/Button/index.tsx b/packages/components/src/components/Button/index.tsx index d5e227e9ca1..eac4364d31f 100644 --- a/packages/components/src/components/Button/index.tsx +++ b/packages/components/src/components/Button/index.tsx @@ -85,6 +85,8 @@ const variantStyles = { export const Button = styled(Element).attrs({ as: 'button' })<{ type?: 'submit' | 'button' | 'reset'; variant?: 'primary' | 'secondary' | 'link' | 'danger'; + disabled?: boolean; + onClick?: any; }>(({ variant = 'primary', ...props }) => css( deepmerge( diff --git a/packages/components/src/components/Element/index.tsx b/packages/components/src/components/Element/index.tsx index ee4f9d9a6b9..b08a9b90145 100644 --- a/packages/components/src/components/Element/index.tsx +++ b/packages/components/src/components/Element/index.tsx @@ -7,6 +7,15 @@ export interface IElementProps { marginY?: number; marginBottom?: number; marginTop?: number; // prefer margin bottom to top + marginLeft?: number; + marginRight?: number; + padding?: number; + paddingX?: number; + paddingY?: number; + paddingBottom?: number; + paddingTop?: number; + paddingLeft?: number; + paddingRight?: number; css?: Object; } @@ -17,6 +26,15 @@ export const Element = styled.div(props => marginY: props.marginY || null, marginBottom: props.marginBottom || null, marginTop: props.marginTop || null, + marginLeft: props.marginLeft || null, + marginRight: props.marginRight || null, + padding: props.padding || null, + paddingX: props.paddingX || null, + paddingY: props.paddingY || null, + paddingBottom: props.paddingBottom || null, + paddingTop: props.paddingTop || null, + paddingLeft: props.paddingLeft || null, + paddingRight: props.paddingRight || null, ...(props.css || {}), }) ); diff --git a/packages/components/src/components/Input/index.tsx b/packages/components/src/components/Input/index.tsx index 4aef7eccbf8..a6b218b3cd8 100644 --- a/packages/components/src/components/Input/index.tsx +++ b/packages/components/src/components/Input/index.tsx @@ -7,7 +7,15 @@ const placeholderStyles = { fontSize: 3, }; -export const Input = styled(Element).attrs({ as: 'input' })( +export const Input = styled(Element).attrs({ as: 'input' })<{ + type?: string; + onBlur?: any; + onChange?: any; + onKeyUp?: any; + placeholder?: string; + ref?: any; + value?: string | number; +}>( css({ height: 6, width: '100%', diff --git a/packages/components/src/components/List/index.tsx b/packages/components/src/components/List/index.tsx index 13a366821dd..8522d14d505 100644 --- a/packages/components/src/components/List/index.tsx +++ b/packages/components/src/components/List/index.tsx @@ -21,14 +21,15 @@ export const ListItem = styled(Stack).attrs({ }) ); -export const ListAction = styled(ListItem)( - css({ - ':hover': { - cursor: 'pointer', - backgroundColor: 'sideBar.hoverBackground', - }, - ':focus-within': { - backgroundColor: 'sideBar.hoverBackground', - }, - }) +export const ListAction = styled(ListItem)<{ disabled?: boolean }>( + ({ disabled }) => + css({ + ':hover': { + cursor: !disabled ? 'pointer' : 'disabled', + backgroundColor: !disabled ? 'sideBar.hoverBackground' : 'inherit', + }, + ':focus-within': { + backgroundColor: !disabled ? 'sideBar.hoverBackground' : 'inherit', + }, + }) ); diff --git a/packages/components/src/components/Select/index.tsx b/packages/components/src/components/Select/index.tsx index b1c9ae8133b..cc7412467b2 100644 --- a/packages/components/src/components/Select/index.tsx +++ b/packages/components/src/components/Select/index.tsx @@ -60,9 +60,17 @@ const SelectWithIcon = styled(Element)( interface ISelectProps { icon?: any; placeholder?: string; + children?: any; + disabled?: boolean; + onChange?: any; + value?: string | number; } -export const Select = ({ icon = null, placeholder = null, ...props }) => { +export const Select = ({ + icon = null, + placeholder = null, + ...props +}: ISelectProps) => { const PrefixIcon = icon || React.Fragment; const SelectContainer = icon ? SelectWithIcon : React.Fragment; diff --git a/packages/sandbox-hooks/console/index.js b/packages/sandbox-hooks/console/index.js index 5890d603ef0..7c6a0a8841d 100644 --- a/packages/sandbox-hooks/console/index.js +++ b/packages/sandbox-hooks/console/index.js @@ -24,6 +24,7 @@ export default function setupConsole() { try { const wrapped = `(${data.command})`; // `new Function` is used to validate Javascript syntax + // eslint-disable-next-line const validate = new Function(wrapped); data.command = wrapped; } catch (e) {