Skip to content

Commit 2139ace

Browse files
authored
New server tab (codesandbox#3397)
* add ports * add control panel * clean up code * server tab * remove button * fix ts * add buttons and fix margin bottom
1 parent 9d68b94 commit 2139ace

File tree

12 files changed

+507
-10
lines changed

12 files changed

+507
-10
lines changed

packages/app/src/app/overmind/utils/items.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ const FILES: INavigationItem = {
3232
id: 'files',
3333
name: 'Explorer',
3434
hasCustomHeader: true,
35-
defaultOpen: true,
3635
};
3736

3837
const GITHUB: INavigationItem = {
@@ -61,6 +60,7 @@ const LIVE: INavigationItem = {
6160
const SERVER: INavigationItem = {
6261
id: 'server',
6362
name: 'Server Control Panel',
63+
defaultOpen: true,
6464
};
6565

6666
export function getDisabledItems(store: any): INavigationItem[] {

packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { NotOwnedSandboxInfo } from './items/NotOwnedSandboxInfo';
2727
import { ProjectInfo } from './items/ProjectInfo';
2828
import { ProjectInfo as ProjectInfoNew } from './screens/ProjectInfo';
2929
import { Deployment as DeploymentNew } from './screens/Deployment/index';
30+
import { Server as ServerNew } from './screens/Server';
3031

3132
import { Server } from './items/Server';
3233
import { SSEDownNotice } from './SSEDownNotice';
@@ -42,7 +43,7 @@ const workspaceTabs = {
4243
deploy: REDESIGNED_SIDEBAR ? DeploymentNew : Deployment,
4344
config: REDESIGNED_SIDEBAR ? ConfigurationFilesNew : ConfigurationFiles,
4445
live: REDESIGNED_SIDEBAR ? LiveNew : Live,
45-
server: Server,
46+
server: REDESIGNED_SIDEBAR ? ServerNew : Server,
4647
more: More,
4748
};
4849

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react';
2+
import {
3+
Collapsible,
4+
Text,
5+
Element,
6+
Button,
7+
Stack,
8+
} from '@codesandbox/components';
9+
import { useOvermind } from 'app/overmind';
10+
11+
const RestartServerIcon = props => (
12+
<svg width={12} height={11} fill="none" viewBox="0 0 12 11" {...props}>
13+
<path
14+
fill="#fff"
15+
fillRule="evenodd"
16+
d="M6.458 9.041a3.875 3.875 0 10-3.832-4.448h1.249L1.937 7.176l-.44-.588-.005.006a4.659 4.659 0 01-.006-.02L0 4.593h1.323a5.167 5.167 0 111.85 4.562l.92-.92a3.858 3.858 0 002.365.806z"
17+
clipRule="evenodd"
18+
/>
19+
<path
20+
fill="#fff"
21+
d="M8.22 4.934a.23.23 0 01-.012.381L5.85 6.782a.23.23 0 01-.35-.194V3.444a.23.23 0 01.362-.187l2.357 1.677z"
22+
/>
23+
</svg>
24+
);
25+
export const Control = () => {
26+
const {
27+
actions: {
28+
server: { restartContainer, restartSandbox },
29+
},
30+
state: {
31+
server: { containerStatus, status },
32+
},
33+
} = useOvermind();
34+
const disconnected = status !== 'connected';
35+
36+
return (
37+
<Collapsible defaultOpen title="Control Container">
38+
<Element paddingX={2}>
39+
<Button
40+
variant="secondary"
41+
disabled={disconnected || containerStatus !== 'sandbox-started'}
42+
onClick={restartSandbox}
43+
>
44+
<Stack gap={2} align="center">
45+
<RestartServerIcon /> <Text>Restart Sandbox</Text>
46+
</Stack>
47+
</Button>
48+
<Button
49+
marginTop={2}
50+
variant="secondary"
51+
disabled={disconnected || containerStatus === 'initializing'}
52+
onClick={restartContainer}
53+
>
54+
<Stack gap={2} align="center">
55+
<RestartServerIcon /> <Text>Restart Server</Text>
56+
</Stack>
57+
</Button>
58+
</Element>
59+
</Collapsible>
60+
);
61+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useOvermind } from 'app/overmind';
2+
import React, { useEffect, useState } from 'react';
3+
4+
import {
5+
Collapsible,
6+
Text,
7+
Element,
8+
List,
9+
ListItem,
10+
Stack,
11+
} from '@codesandbox/components';
12+
import { DeleteIcon, EditIcon } from './Icons';
13+
import { VarForm } from './VarForm';
14+
15+
export const EnvVars = () => {
16+
const [editMode, setEditMode] = useState(false);
17+
const {
18+
actions: { editor },
19+
state: {
20+
editor: { currentSandbox },
21+
},
22+
} = useOvermind();
23+
24+
useEffect(() => {
25+
editor.fetchEnvironmentVariables();
26+
}, [editor]);
27+
28+
const deleteEnv = name => {
29+
editor.deleteEnvironmentVariable({ name });
30+
};
31+
const envVars = currentSandbox.environmentVariables;
32+
33+
return (
34+
<Collapsible title="Secret Keys" defaultOpen>
35+
<Element paddingX={2}>
36+
<Text variant="muted" block>
37+
Secrets are available as environment variables. They are kept private
38+
and will not be transferred between forks.
39+
</Text>
40+
</Element>
41+
{envVars ? (
42+
<List marginTop={4}>
43+
{Object.keys(envVars).map(keyName => (
44+
<>
45+
{editMode ? (
46+
<VarForm
47+
name={keyName}
48+
value={envVars[keyName]}
49+
onCancel={() => setEditMode(false)}
50+
onSubmit={({ name, value }) => {
51+
editor.updateEnvironmentVariables({ name, value });
52+
setEditMode(false);
53+
}}
54+
/>
55+
) : null}
56+
<ListItem justify="space-between" marginTop={editMode ? 4 : 0}>
57+
<Text>{keyName}</Text>
58+
{!editMode ? (
59+
<Stack gap={2}>
60+
<EditIcon
61+
style={{ cursor: 'pointer' }}
62+
onClick={() => setEditMode(true)}
63+
/>
64+
<DeleteIcon
65+
style={{ cursor: 'pointer' }}
66+
onClick={() => deleteEnv(keyName)}
67+
/>
68+
</Stack>
69+
) : null}
70+
</ListItem>
71+
</>
72+
))}
73+
</List>
74+
) : null}
75+
76+
<VarForm
77+
onSubmit={({ name, value }) =>
78+
editor.updateEnvironmentVariables({ name, value })
79+
}
80+
/>
81+
</Collapsible>
82+
);
83+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
3+
export const EditIcon = props => (
4+
<svg width={16} height={16} fill="none" viewBox="0 0 16 16" {...props}>
5+
<path
6+
fill="#757575"
7+
d="M1.97 14l3.034-.787 7.64-7.64-2.247-2.247-7.64 7.64L1.971 14zM11.52 2.203l-.561.562 2.247 2.247.562-.562a.795.795 0 000-1.124l-1.124-1.123a.794.794 0 00-1.123 0z"
8+
/>
9+
</svg>
10+
);
11+
12+
export const DeleteIcon = props => (
13+
<svg width={16} height={16} fill="none" viewBox="0 0 16 16" {...props}>
14+
<path
15+
fill="#757575"
16+
d="M13 3.97L12.09 3 8 7.364 3.91 3 3 3.97l4.09 4.363L3 12.697l.91.97L8 9.303l4.09 4.364.91-.97-4.09-4.364L13 3.97z"
17+
/>
18+
</svg>
19+
);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React from 'react';
2+
import {
3+
Collapsible,
4+
List,
5+
ListAction,
6+
Text,
7+
Stack,
8+
} from '@codesandbox/components';
9+
import { css } from '@styled-system/css';
10+
import { useOvermind } from 'app/overmind';
11+
import { ServerPort } from '@codesandbox/common/lib/types';
12+
import BrowserIcon from 'react-icons/lib/go/browser';
13+
14+
export const Ports = () => {
15+
const {
16+
actions: {
17+
server: { onBrowserTabOpened, onBrowserFromPortOpened },
18+
},
19+
state: {
20+
server: { ports },
21+
editor: { currentSandbox: sandbox },
22+
},
23+
} = useOvermind();
24+
25+
const openPort = (port: ServerPort) => {
26+
onBrowserFromPortOpened({ port });
27+
};
28+
29+
const openGraphiQLPort = () => {
30+
const url = sandbox.template === 'gridsome' ? '/___explore' : '/___graphql';
31+
onBrowserTabOpened({
32+
closeable: true,
33+
options: {
34+
url,
35+
title: 'GraphiQL',
36+
},
37+
});
38+
};
39+
40+
return (
41+
<Collapsible title="Open Ports" defaultOpen>
42+
<List>
43+
{ports.length ? (
44+
ports.map((port: ServerPort) => (
45+
<ListAction onClick={() => openPort(port)}>
46+
<Stack
47+
justify="space-between"
48+
css={css({
49+
width: '100%',
50+
})}
51+
>
52+
<Stack align="center">
53+
<BrowserIcon />
54+
<Text marginLeft={2}>{port.name || port.port}</Text>
55+
</Stack>
56+
{port.main && <Text weight="medium">main</Text>}
57+
</Stack>
58+
</ListAction>
59+
))
60+
) : (
61+
<Text block variant="muted" paddingX={2}>
62+
No ports are opened. Maybe the server is still starting or it doesn
63+
{"'"}t open any ports.
64+
</Text>
65+
)}
66+
67+
{['gatsby', 'gridsome'].includes(sandbox.template) && ports.length ? (
68+
<ListAction onClick={openGraphiQLPort}>
69+
<Stack align="center">
70+
<BrowserIcon />
71+
<Text marginLeft={2}>GraphiQL</Text>
72+
</Stack>
73+
</ListAction>
74+
) : null}
75+
</List>
76+
</Collapsible>
77+
);
78+
};
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import styled, { css } from 'styled-components';
3+
import { Stack, Text, Element } from '@codesandbox/components';
4+
import {
5+
SSEContainerStatus,
6+
SSEManagerStatus,
7+
} from '@codesandbox/common/lib/types';
8+
import { useOvermind } from 'app/overmind';
9+
10+
type StatusCircleProps = { theme: any; color: string };
11+
12+
const StatusCircle = styled.div`
13+
${({ theme, color }: StatusCircleProps) => css`
14+
border-radius: 50%;
15+
background-color: ${color};
16+
width: ${theme.space[2]}px;
17+
height: ${theme.space[2]}px;
18+
`}
19+
`;
20+
21+
const STATUS_MESSAGES = {
22+
initializing: 'Container is starting...',
23+
starting: 'Sandbox is starting...',
24+
started: 'Connected to sandbox',
25+
error: 'A sandbox error occurred',
26+
hibernated: 'Sandbox hibernated',
27+
};
28+
29+
const STATUS_COLOR = {
30+
disconnected: '#FD2439',
31+
connected: '#4CFF00',
32+
initializing: '#FFD399',
33+
hibernated: '#FF662E',
34+
error: '#FD2439',
35+
};
36+
37+
function getContainerStatusMessageAndColor(
38+
containerStatus: SSEContainerStatus
39+
) {
40+
switch (containerStatus) {
41+
case 'initializing':
42+
return { color: '#FFD399', message: STATUS_MESSAGES.initializing };
43+
case 'container-started':
44+
case 'stopped':
45+
return { color: '#FFD399', message: STATUS_MESSAGES.starting };
46+
case 'sandbox-started':
47+
return { color: '#4CFF00', message: STATUS_MESSAGES.started };
48+
case 'error':
49+
return { color: '#FD2439', message: STATUS_MESSAGES.error };
50+
case 'hibernated':
51+
return { color: '#FF662E', message: STATUS_MESSAGES.hibernated };
52+
default:
53+
return undefined;
54+
}
55+
}
56+
57+
function getManagerStatusMessageAndColor(managerStatus: SSEManagerStatus) {
58+
switch (managerStatus) {
59+
case 'connected':
60+
case 'disconnected':
61+
return undefined;
62+
case 'initializing':
63+
return {
64+
message: STATUS_MESSAGES.initializing,
65+
color: STATUS_COLOR.initializing,
66+
};
67+
default:
68+
return undefined;
69+
}
70+
}
71+
72+
export const Status = () => {
73+
const {
74+
state: {
75+
server: { containerStatus, status: managerStatus },
76+
},
77+
} = useOvermind();
78+
const { color, message } =
79+
getManagerStatusMessageAndColor(managerStatus) ||
80+
getContainerStatusMessageAndColor(containerStatus);
81+
82+
return (
83+
<Stack align="center">
84+
<Element marginX={2}>
85+
<StatusCircle color={color} />
86+
</Element>
87+
<Text marginLeft={2}>{message}</Text>
88+
</Stack>
89+
);
90+
};

0 commit comments

Comments
 (0)