Skip to content

Commit 54c30be

Browse files
committed
Add FE
1 parent f741523 commit 54c30be

File tree

7 files changed

+223
-149
lines changed

7 files changed

+223
-149
lines changed

codersdk/workspacebuilds.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,6 @@ type WorkspaceBuildParameter struct {
102102
Value string `json:"value"`
103103
}
104104

105-
type StartupScriptLog struct {
106-
AgentID uuid.UUID `json:"agent_id"`
107-
JobID uuid.UUID `json:"job_id"`
108-
Output string `json:"output"`
109-
}
110-
111105
// WorkspaceBuild returns a single workspace build for a workspace.
112106
// If history is "", the latest version is returned.
113107
func (c *Client) WorkspaceBuild(ctx context.Context, id uuid.UUID) (WorkspaceBuild, error) {

site/src/api/api.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,15 @@ export const getWorkspaceBuildLogs = async (
668668
return response.data
669669
}
670670

671+
export const getWorkspaceAgentStartupLogs = async (
672+
agentID: string,
673+
): Promise<TypesGen.WorkspaceAgentStartupLog[]> => {
674+
const response = await axios.get<TypesGen.WorkspaceAgentStartupLog[]>(
675+
`/api/v2/workspaceagents/${agentID}/startup-logs`,
676+
)
677+
return response.data
678+
}
679+
671680
export const putWorkspaceExtension = async (
672681
workspaceId: string,
673682
newDeadline: dayjs.Dayjs,

site/src/components/Resources/AgentRow.tsx

Lines changed: 111 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import { AgentStatus } from "./AgentStatus"
1414
import { AppLinkSkeleton } from "components/AppLink/AppLinkSkeleton"
1515
import { useTranslation } from "react-i18next"
1616
import { VSCodeDesktopButton } from "components/VSCodeDesktopButton/VSCodeDesktopButton"
17+
import { useMachine } from "@xstate/react"
18+
import { workspaceAgentLogsMachine } from "xServices/workspaceAgentLogs/workspaceAgentLogsXService"
19+
import { Line, Logs } from "components/Logs/Logs"
1720

1821
export interface AgentRowProps {
1922
agent: WorkspaceAgent
@@ -38,108 +41,126 @@ export const AgentRow: FC<AgentRowProps> = ({
3841
}) => {
3942
const styles = useStyles()
4043
const { t } = useTranslation("agent")
44+
const [logsMachine] = useMachine(workspaceAgentLogsMachine, {
45+
context: { agentID: agent.id },
46+
})
4147

4248
return (
43-
<Stack
44-
key={agent.id}
45-
direction="row"
46-
alignItems="center"
47-
justifyContent="space-between"
48-
className={styles.agentRow}
49-
spacing={4}
50-
>
51-
<Stack direction="row" alignItems="baseline">
52-
<div className={styles.agentStatusWrapper}>
53-
<AgentStatus agent={agent} />
54-
</div>
55-
<div>
56-
<div className={styles.agentName}>{agent.name}</div>
57-
<Stack
58-
direction="row"
59-
alignItems="baseline"
60-
className={styles.agentData}
61-
spacing={1}
62-
>
63-
<span className={styles.agentOS}>{agent.operating_system}</span>
64-
65-
<Maybe condition={agent.status === "connected"}>
66-
<AgentVersion
67-
agent={agent}
68-
serverVersion={serverVersion}
69-
onUpdate={onUpdateAgent}
70-
/>
71-
</Maybe>
72-
73-
<AgentLatency agent={agent} />
74-
75-
<Maybe condition={agent.status === "connecting"}>
76-
<Skeleton width={160} variant="text" />
77-
<Skeleton width={36} variant="text" />
78-
</Maybe>
79-
80-
<Maybe condition={agent.status === "timeout"}>
81-
{t("unableToConnect")}
82-
</Maybe>
83-
</Stack>
84-
</div>
85-
</Stack>
86-
49+
<Stack direction="column" key={agent.id} spacing={0}>
8750
<Stack
8851
direction="row"
8952
alignItems="center"
90-
spacing={0.5}
91-
wrap="wrap"
92-
maxWidth="750px"
53+
justifyContent="space-between"
54+
className={styles.agentRow}
55+
spacing={4}
9356
>
94-
{showApps && agent.status === "connected" && (
95-
<>
96-
{agent.apps.map((app) => (
97-
<AppLink
98-
key={app.slug}
99-
appsHost={applicationsHost}
100-
app={app}
101-
agent={agent}
102-
workspace={workspace}
103-
/>
104-
))}
105-
106-
<TerminalLink
107-
workspaceName={workspace.name}
108-
agentName={agent.name}
109-
userName={workspace.owner_name}
110-
/>
111-
{!hideSSHButton && (
112-
<SSHButton
57+
<Stack direction="row" alignItems="baseline">
58+
<div className={styles.agentStatusWrapper}>
59+
<AgentStatus agent={agent} />
60+
</div>
61+
<div>
62+
<div className={styles.agentName}>{agent.name}</div>
63+
<Stack
64+
direction="row"
65+
alignItems="baseline"
66+
className={styles.agentData}
67+
spacing={1}
68+
>
69+
<span className={styles.agentOS}>{agent.operating_system}</span>
70+
71+
<Maybe condition={agent.status === "connected"}>
72+
<AgentVersion
73+
agent={agent}
74+
serverVersion={serverVersion}
75+
onUpdate={onUpdateAgent}
76+
/>
77+
</Maybe>
78+
79+
<AgentLatency agent={agent} />
80+
81+
<Maybe condition={agent.status === "connecting"}>
82+
<Skeleton width={160} variant="text" />
83+
<Skeleton width={36} variant="text" />
84+
</Maybe>
85+
86+
<Maybe condition={agent.status === "timeout"}>
87+
{t("unableToConnect")}
88+
</Maybe>
89+
</Stack>
90+
</div>
91+
</Stack>
92+
93+
<Stack
94+
direction="row"
95+
alignItems="center"
96+
spacing={0.5}
97+
wrap="wrap"
98+
maxWidth="750px"
99+
>
100+
{showApps && agent.status === "connected" && (
101+
<>
102+
{agent.apps.map((app) => (
103+
<AppLink
104+
key={app.slug}
105+
appsHost={applicationsHost}
106+
app={app}
107+
agent={agent}
108+
workspace={workspace}
109+
/>
110+
))}
111+
112+
<TerminalLink
113113
workspaceName={workspace.name}
114114
agentName={agent.name}
115-
/>
116-
)}
117-
{!hideVSCodeDesktopButton && (
118-
<VSCodeDesktopButton
119115
userName={workspace.owner_name}
120-
workspaceName={workspace.name}
121-
agentName={agent.name}
122-
folderPath={agent.expanded_directory}
123-
/>
124-
)}
125-
{applicationsHost !== undefined && applicationsHost !== "" && (
126-
<PortForwardButton
127-
host={applicationsHost}
128-
workspaceName={workspace.name}
129-
agentId={agent.id}
130-
agentName={agent.name}
131-
username={workspace.owner_name}
132116
/>
117+
{!hideSSHButton && (
118+
<SSHButton
119+
workspaceName={workspace.name}
120+
agentName={agent.name}
121+
/>
122+
)}
123+
{!hideVSCodeDesktopButton && (
124+
<VSCodeDesktopButton
125+
userName={workspace.owner_name}
126+
workspaceName={workspace.name}
127+
agentName={agent.name}
128+
folderPath={agent.expanded_directory}
129+
/>
130+
)}
131+
{applicationsHost !== undefined && applicationsHost !== "" && (
132+
<PortForwardButton
133+
host={applicationsHost}
134+
workspaceName={workspace.name}
135+
agentId={agent.id}
136+
agentName={agent.name}
137+
username={workspace.owner_name}
138+
/>
139+
)}
140+
</>
141+
)}
142+
{showApps && agent.status === "connecting" && (
143+
<>
144+
<AppLinkSkeleton width={84} />
145+
<AppLinkSkeleton width={112} />
146+
</>
147+
)}
148+
</Stack>
149+
</Stack>
150+
151+
<div>
152+
{logsMachine.context.startupLogs && (
153+
<Logs
154+
lines={logsMachine.context.startupLogs.map(
155+
(log): Line => ({
156+
level: "info",
157+
output: log.output,
158+
time: log.created_at,
159+
}),
133160
)}
134-
</>
135-
)}
136-
{showApps && agent.status === "connecting" && (
137-
<>
138-
<AppLinkSkeleton width={84} />
139-
<AppLinkSkeleton width={112} />
140-
</>
161+
/>
141162
)}
142-
</Stack>
163+
</div>
143164
</Stack>
144165
)
145166
}

site/src/components/Workspace/Workspace.tsx

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
import { makeStyles } from "@material-ui/core/styles"
2+
import { Avatar } from "components/Avatar/Avatar"
3+
import { AgentRow } from "components/Resources/AgentRow"
4+
import {
5+
ActiveTransition,
6+
WorkspaceBuildProgress
7+
} from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
28
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
3-
import { FC, useEffect, useState } from "react"
9+
import { FC } from "react"
410
import { useNavigate } from "react-router-dom"
511
import * as TypesGen from "../../api/typesGenerated"
12+
import { AlertBanner } from "../AlertBanner/AlertBanner"
613
import { BuildsTable } from "../BuildsTable/BuildsTable"
714
import { Margins } from "../Margins/Margins"
815
import {
916
PageHeader,
1017
PageHeaderSubtitle,
11-
PageHeaderTitle,
18+
PageHeaderTitle
1219
} from "../PageHeader/PageHeader"
1320
import { Resources } from "../Resources/Resources"
1421
import { Stack } from "../Stack/Stack"
1522
import { WorkspaceActions } from "../WorkspaceActions/WorkspaceActions"
1623
import { WorkspaceDeletedBanner } from "../WorkspaceDeletedBanner/WorkspaceDeletedBanner"
1724
import { WorkspaceScheduleButton } from "../WorkspaceScheduleButton/WorkspaceScheduleButton"
1825
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
19-
import { AlertBanner } from "../AlertBanner/AlertBanner"
20-
import {
21-
ActiveTransition,
22-
WorkspaceBuildProgress,
23-
} from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
24-
import { AgentRow } from "components/Resources/AgentRow"
25-
import { Avatar } from "components/Avatar/Avatar"
26-
import { CodeBlock } from "components/CodeBlock/CodeBlock"
2726

2827
export enum WorkspaceErrors {
2928
GET_BUILDS_ERROR = "getBuildsError",
@@ -58,7 +57,6 @@ export interface WorkspaceProps {
5857
template?: TypesGen.Template
5958
templateParameters?: TypesGen.TemplateVersionParameter[]
6059
quota_budget?: number
61-
startupScriptLogs?: TypesGen.StartupScriptLog[] | Error | unknown
6260
}
6361

6462
/**
@@ -86,7 +84,6 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
8684
template,
8785
templateParameters,
8886
quota_budget,
89-
startupScriptLogs,
9087
}) => {
9188
const styles = useStyles()
9289
const navigate = useNavigate()
@@ -214,19 +211,6 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
214211
/>
215212
)}
216213

217-
{typeof startupScriptLogs !== "undefined" &&
218-
(Array.isArray(startupScriptLogs) ? (
219-
startupScriptLogs.map((log) => (
220-
<CodeBlock
221-
key={log.agent_id}
222-
className={styles.logs}
223-
lines={log.output ? log.output.split("\n") : "No output"}
224-
/>
225-
))
226-
) : (
227-
<AlertBanner severity="error" error={startupScriptLogs} />
228-
))}
229-
230214
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
231215
<AlertBanner
232216
severity="error"

site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { makeStyles } from "@material-ui/core/styles"
22
import { useMachine } from "@xstate/react"
3-
import * as API from "api/api"
43
import { AlertBanner } from "components/AlertBanner/AlertBanner"
54
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
6-
import { FC, useEffect, useState } from "react"
7-
import { useParams } from "react-router-dom"
85
import { Loader } from "components/Loader/Loader"
6+
import { FC, useEffect } from "react"
7+
import { useParams } from "react-router-dom"
98
import { firstOrItem } from "util/array"
9+
import { quotaMachine } from "xServices/quotas/quotasXService"
1010
import { workspaceMachine } from "xServices/workspace/workspaceXService"
1111
import { WorkspaceReadyPage } from "./WorkspaceReadyPage"
12-
import { quotaMachine } from "xServices/quotas/quotasXService"
1312

1413
export const WorkspacePage: FC = () => {
1514
const { username: usernameQueryParam, workspace: workspaceQueryParam } =
@@ -28,10 +27,6 @@ export const WorkspacePage: FC = () => {
2827
const { getQuotaError } = quotaState.context
2928
const styles = useStyles()
3029

31-
const [startupScriptLogs, setStartupScriptLogs] = useState<
32-
Record<string, string> | Error | undefined | unknown
33-
>(undefined)
34-
3530
/**
3631
* Get workspace, template, and organization on mount and whenever workspaceId changes.
3732
* workspaceSend should not change.
@@ -46,22 +41,6 @@ export const WorkspacePage: FC = () => {
4641
username && quotaSend({ type: "GET_QUOTA", username })
4742
}, [username, quotaSend])
4843

49-
// Get startup logs once we have agents or when the agents change.
50-
// TODO: Should use xstate? Or that new thing?
51-
// TODO: Does not stream yet.
52-
// TODO: Should maybe add to the existing SSE endpoint instead?
53-
useEffect(() => {
54-
if (workspace?.latest_build) {
55-
API.getStartupScriptLogs(workspace.latest_build.id)
56-
.then((logs) => {
57-
setStartupScriptLogs(logs)
58-
})
59-
.catch((error) => {
60-
setStartupScriptLogs(error)
61-
})
62-
}
63-
}, [workspace])
64-
6544
return (
6645
<ChooseOne>
6746
<Cond condition={workspaceState.matches("error")}>
@@ -97,7 +76,6 @@ export const WorkspacePage: FC = () => {
9776
workspaceState={workspaceState}
9877
quotaState={quotaState}
9978
workspaceSend={workspaceSend}
100-
startupScriptLogs={startupScriptLogs}
10179
/>
10280
</Cond>
10381
<Cond>

0 commit comments

Comments
 (0)