Skip to content

Commit 166bc27

Browse files
feature: Add SSH button in the agent access column (coder#2931)
1 parent 0645176 commit 166bc27

File tree

3 files changed

+143
-10
lines changed

3 files changed

+143
-10
lines changed

site/src/components/Resources/Resources.tsx

+11-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { FC } from "react"
99
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
1010
import { getDisplayAgentStatus } from "../../util/workspace"
1111
import { AppLink } from "../AppLink/AppLink"
12+
import { SSHButton } from "../SSHButton/SSHButton"
1213
import { Stack } from "../Stack/Stack"
1314
import { TableHeaderRow } from "../TableHeaders/TableHeaders"
1415
import { TerminalLink } from "../TerminalLink/TerminalLink"
@@ -107,18 +108,17 @@ export const Resources: FC<ResourcesProps> = ({
107108
<span style={{ color: agentStatus.color }}>{agentStatus.status}</span>
108109
</div>
109110
</TableCell>
110-
{canUpdateWorkspace && (
111-
<TableCell>
112-
<div className={styles.accessLinks}>
113-
{agent.status === "connected" && (
111+
<TableCell>
112+
<>
113+
{canUpdateWorkspace && agent.status === "connected" && (
114+
<div className={styles.accessLinks}>
115+
<SSHButton workspaceName={workspace.name} agentName={agent.name} />
114116
<TerminalLink
115117
workspaceName={workspace.name}
116118
agentName={agent.name}
117119
userName={workspace.owner_name}
118120
/>
119-
)}
120-
{agent.status === "connected" &&
121-
agent.apps.map((app) => (
121+
{agent.apps.map((app) => (
122122
<AppLink
123123
key={app.name}
124124
appIcon={app.icon}
@@ -127,9 +127,10 @@ export const Resources: FC<ResourcesProps> = ({
127127
workspaceName={workspace.name}
128128
/>
129129
))}
130-
</div>
131-
</TableCell>
132-
)}
130+
</div>
131+
)}
132+
</>
133+
</TableCell>
133134
</TableRow>
134135
)
135136
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Story } from "@storybook/react"
2+
import { MockWorkspace, MockWorkspaceAgent } from "../../testHelpers/renderHelpers"
3+
import { SSHButton, SSHButtonProps } from "./SSHButton"
4+
5+
export default {
6+
title: "components/SSHButton",
7+
component: SSHButton,
8+
}
9+
10+
const Template: Story<SSHButtonProps> = (args) => <SSHButton {...args} />
11+
12+
export const Closed = Template.bind({})
13+
Closed.args = {
14+
workspaceName: MockWorkspace.name,
15+
agentName: MockWorkspaceAgent.name,
16+
}
17+
18+
export const Opened = Template.bind({})
19+
Opened.args = {
20+
workspaceName: MockWorkspace.name,
21+
agentName: MockWorkspaceAgent.name,
22+
defaultIsOpen: true,
23+
}
+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import Button from "@material-ui/core/Button"
2+
import Popover from "@material-ui/core/Popover"
3+
import { makeStyles } from "@material-ui/core/styles"
4+
import CloudIcon from "@material-ui/icons/CloudOutlined"
5+
import { useRef, useState } from "react"
6+
import { CodeExample } from "../CodeExample/CodeExample"
7+
import { Stack } from "../Stack/Stack"
8+
import { HelpTooltipLink, HelpTooltipLinksGroup, HelpTooltipText } from "../Tooltips/HelpTooltip"
9+
10+
export interface SSHButtonProps {
11+
workspaceName: string
12+
agentName: string
13+
defaultIsOpen?: boolean
14+
}
15+
16+
export const SSHButton: React.FC<SSHButtonProps> = ({
17+
workspaceName,
18+
agentName,
19+
defaultIsOpen = false,
20+
}) => {
21+
const anchorRef = useRef<HTMLButtonElement>(null)
22+
const [isOpen, setIsOpen] = useState(defaultIsOpen)
23+
const id = isOpen ? "schedule-popover" : undefined
24+
const styles = useStyles()
25+
26+
const onClose = () => {
27+
setIsOpen(false)
28+
}
29+
30+
return (
31+
<>
32+
<Button
33+
startIcon={<CloudIcon />}
34+
size="small"
35+
ref={anchorRef}
36+
onClick={() => {
37+
setIsOpen(true)
38+
}}
39+
>
40+
SSH
41+
</Button>
42+
<Popover
43+
classes={{ paper: styles.popoverPaper }}
44+
id={id}
45+
open={isOpen}
46+
anchorEl={anchorRef.current}
47+
onClose={onClose}
48+
anchorOrigin={{
49+
vertical: "bottom",
50+
horizontal: "left",
51+
}}
52+
transformOrigin={{
53+
vertical: "top",
54+
horizontal: "left",
55+
}}
56+
>
57+
<HelpTooltipText>Run the following commands to connect with SSH:</HelpTooltipText>
58+
59+
<Stack spacing={0.5} className={styles.codeExamples}>
60+
<div>
61+
<HelpTooltipText>
62+
<strong className={styles.codeExampleLabel}>
63+
Configure ssh{" "}
64+
<span className={styles.textHelper}>
65+
- only needs to be run once, or after managing workspaces
66+
</span>
67+
</strong>
68+
</HelpTooltipText>
69+
<CodeExample code="coder config-ssh" />
70+
</div>
71+
72+
<div>
73+
<HelpTooltipText>
74+
<strong className={styles.codeExampleLabel}>Connect to the agent</strong>
75+
</HelpTooltipText>
76+
<CodeExample code={`ssh coder.${workspaceName}.${agentName}`} />
77+
</div>
78+
</Stack>
79+
80+
<HelpTooltipLinksGroup>
81+
<HelpTooltipLink href="#">Install Coder CLI</HelpTooltipLink>
82+
<HelpTooltipLink href="#">Configuring VS Code</HelpTooltipLink>
83+
<HelpTooltipLink href="#">SSH configuration</HelpTooltipLink>
84+
</HelpTooltipLinksGroup>
85+
</Popover>
86+
</>
87+
)
88+
}
89+
90+
const useStyles = makeStyles((theme) => ({
91+
popoverPaper: {
92+
padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(3)}px`,
93+
width: theme.spacing(38),
94+
color: theme.palette.text.secondary,
95+
marginTop: theme.spacing(0.25),
96+
},
97+
98+
codeExamples: {
99+
marginTop: theme.spacing(1.5),
100+
},
101+
102+
codeExampleLabel: {
103+
fontSize: 12,
104+
},
105+
106+
textHelper: {
107+
fontWeight: 400,
108+
},
109+
}))

0 commit comments

Comments
 (0)