Skip to content

Commit 3632ac8

Browse files
refactor(site): Update workspace header (#7433)
1 parent 7f02fa6 commit 3632ac8

File tree

7 files changed

+293
-149
lines changed

7 files changed

+293
-149
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import { FC, PropsWithChildren } from "react"
3+
4+
export const FullWidthPageHeader: FC<PropsWithChildren> = ({ children }) => {
5+
const styles = useStyles()
6+
7+
return (
8+
<header className={styles.header} data-testid="header">
9+
{children}
10+
</header>
11+
)
12+
}
13+
14+
export const PageHeaderActions: FC<PropsWithChildren> = ({ children }) => {
15+
const styles = useStyles()
16+
return <div className={styles.actions}>{children}</div>
17+
}
18+
19+
export const PageHeaderTitle: FC<PropsWithChildren> = ({ children }) => {
20+
const styles = useStyles()
21+
return <h1 className={styles.title}>{children}</h1>
22+
}
23+
24+
export const PageHeaderSubtitle: FC<PropsWithChildren> = ({ children }) => {
25+
const styles = useStyles()
26+
return <span className={styles.subtitle}>{children}</span>
27+
}
28+
29+
const useStyles = makeStyles((theme) => ({
30+
header: {
31+
padding: theme.spacing(3),
32+
background: theme.palette.background.paper,
33+
borderBottom: `1px solid ${theme.palette.divider}`,
34+
display: "flex",
35+
alignItems: "center",
36+
gap: theme.spacing(6),
37+
position: "sticky",
38+
top: 0,
39+
zIndex: 10,
40+
flexWrap: "wrap",
41+
42+
[theme.breakpoints.down("md")]: {
43+
position: "unset",
44+
alignItems: "flex-start",
45+
},
46+
[theme.breakpoints.down("sm")]: {
47+
flexDirection: "column",
48+
},
49+
},
50+
actions: {
51+
marginLeft: "auto",
52+
[theme.breakpoints.down("sm")]: {
53+
marginLeft: "unset",
54+
},
55+
},
56+
title: {
57+
fontSize: 18,
58+
fontWeight: 500,
59+
margin: 0,
60+
},
61+
subtitle: {
62+
fontSize: 14,
63+
color: theme.palette.text.secondary,
64+
marginTop: theme.spacing(0.25),
65+
display: "block",
66+
},
67+
}))

site/src/components/Stats/Stats.tsx

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
import { makeStyles } from "@material-ui/core/styles"
22
import { ComponentProps, FC, PropsWithChildren } from "react"
3+
import { combineClasses } from "utils/combineClasses"
34

4-
export const Stats: FC<PropsWithChildren<ComponentProps<"div">>> = (props) => {
5+
export const Stats: FC<ComponentProps<"div">> = (props) => {
56
const styles = useStyles()
6-
return <div className={styles.stats} {...props} />
7+
return (
8+
<div
9+
{...props}
10+
className={combineClasses([styles.stats, props.className])}
11+
/>
12+
)
713
}
814

9-
export const StatsItem: FC<{
10-
label: string
11-
value: string | number | JSX.Element
12-
}> = ({ label, value }) => {
15+
export const StatsItem: FC<
16+
{
17+
label: string
18+
value: string | number | JSX.Element
19+
} & ComponentProps<"div">
20+
> = ({ label, value, ...divProps }) => {
1321
const styles = useStyles()
1422

1523
return (
16-
<div className={styles.statItem}>
24+
<div
25+
{...divProps}
26+
className={combineClasses([styles.statItem, divProps.className])}
27+
>
1728
<span className={styles.statsLabel}>{label}:</span>
1829
<span className={styles.statsValue}>{value}</span>
1930
</div>

site/src/components/Workspace/Workspace.tsx

+113-117
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ import {
88
ActiveTransition,
99
WorkspaceBuildProgress,
1010
} from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
11-
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
1211
import { FC } from "react"
1312
import { useTranslation } from "react-i18next"
1413
import { useNavigate } from "react-router-dom"
1514
import * as TypesGen from "../../api/typesGenerated"
1615
import { AlertBanner } from "../AlertBanner/AlertBanner"
1716
import { BuildsTable } from "../BuildsTable/BuildsTable"
1817
import { Margins } from "../Margins/Margins"
19-
import {
20-
PageHeader,
21-
PageHeaderSubtitle,
22-
PageHeaderTitle,
23-
} from "../PageHeader/PageHeader"
2418
import { Resources } from "../Resources/Resources"
2519
import { Stack } from "../Stack/Stack"
2620
import { WorkspaceActions } from "../WorkspaceActions/WorkspaceActions"
2721
import { WorkspaceDeletedBanner } from "../WorkspaceDeletedBanner/WorkspaceDeletedBanner"
2822
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
23+
import {
24+
FullWidthPageHeader,
25+
PageHeaderActions,
26+
PageHeaderTitle,
27+
PageHeaderSubtitle,
28+
} from "components/PageHeader/FullWidthPageHeader"
2929

3030
export enum WorkspaceErrors {
3131
GET_BUILDS_ERROR = "getBuildsError",
@@ -125,64 +125,22 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
125125
transitionStats = ActiveTransition(template, workspace)
126126
}
127127
return (
128-
<Margins>
129-
<PageHeader
130-
actions={
131-
<Stack direction="row" spacing={1} className={styles.actions}>
132-
<WorkspaceActions
133-
workspaceStatus={workspace.latest_build.status}
134-
isOutdated={workspace.outdated}
135-
handleStart={handleStart}
136-
handleStop={handleStop}
137-
handleRestart={handleRestart}
138-
handleDelete={handleDelete}
139-
handleUpdate={handleUpdate}
140-
handleCancel={handleCancel}
141-
handleSettings={handleSettings}
142-
handleChangeVersion={handleChangeVersion}
143-
canChangeVersions={canChangeVersions}
144-
isUpdating={isUpdating}
145-
isRestarting={isRestarting}
146-
/>
147-
</Stack>
148-
}
149-
>
128+
<>
129+
<FullWidthPageHeader>
150130
<Stack direction="row" spacing={3} alignItems="center">
151131
<Avatar
152-
size="xl"
132+
size="md"
153133
src={workspace.template_icon}
154134
variant={workspace.template_icon ? "square" : undefined}
155135
fitImage={Boolean(workspace.template_icon)}
156136
>
157137
{workspace.name}
158138
</Avatar>
159139
<div>
160-
<PageHeaderTitle>
161-
{workspace.name}
162-
<WorkspaceStatusBadge
163-
build={workspace.latest_build}
164-
className={styles.statusBadge}
165-
/>
166-
</PageHeaderTitle>
167-
<PageHeaderSubtitle condensed>
168-
{workspace.owner_name}
169-
</PageHeaderSubtitle>
140+
<PageHeaderTitle>{workspace.name}</PageHeaderTitle>
141+
<PageHeaderSubtitle>{workspace.owner_name}</PageHeaderSubtitle>
170142
</div>
171143
</Stack>
172-
</PageHeader>
173-
174-
<Stack
175-
direction="column"
176-
className={styles.firstColumnSpacer}
177-
spacing={4}
178-
>
179-
{buildError}
180-
{cancellationError}
181-
182-
<WorkspaceDeletedBanner
183-
workspace={workspace}
184-
handleClick={() => navigate(`/templates`)}
185-
/>
186144

187145
<WorkspaceStats
188146
workspace={workspace}
@@ -195,83 +153,121 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
195153
onDeadlinePlus={scheduleProps.onDeadlinePlus}
196154
/>
197155

198-
{failedBuildLogs && (
199-
<Stack>
200-
<AlertBanner severity="error">
201-
<Stack
202-
className={styles.fullWidth}
203-
direction="row"
204-
alignItems="center"
205-
justifyContent="space-between"
206-
>
207-
<Stack spacing={0}>
208-
<span>Workspace build failed</span>
209-
<span className={styles.errorDetails}>
210-
{workspace.latest_build.job.error}
211-
</span>
212-
</Stack>
156+
<PageHeaderActions>
157+
<WorkspaceActions
158+
workspaceStatus={workspace.latest_build.status}
159+
isOutdated={workspace.outdated}
160+
handleStart={handleStart}
161+
handleStop={handleStop}
162+
handleRestart={handleRestart}
163+
handleDelete={handleDelete}
164+
handleUpdate={handleUpdate}
165+
handleCancel={handleCancel}
166+
handleSettings={handleSettings}
167+
handleChangeVersion={handleChangeVersion}
168+
canChangeVersions={canChangeVersions}
169+
isUpdating={isUpdating}
170+
isRestarting={isRestarting}
171+
/>
172+
</PageHeaderActions>
173+
</FullWidthPageHeader>
213174

214-
{canUpdateTemplate && (
215-
<div>
216-
<Button
217-
onClick={handleBuildRetry}
218-
startIcon={<RefreshOutlined />}
219-
size="small"
220-
variant="outlined"
221-
>
222-
{t("actionButton.retryDebugMode")}
223-
</Button>
224-
</div>
225-
)}
226-
</Stack>
227-
</AlertBanner>
228-
<WorkspaceBuildLogs logs={failedBuildLogs} />
229-
</Stack>
230-
)}
175+
<Margins className={styles.content}>
176+
<Stack
177+
direction="column"
178+
className={styles.firstColumnSpacer}
179+
spacing={4}
180+
>
181+
{buildError}
182+
{cancellationError}
231183

232-
{transitionStats !== undefined && (
233-
<WorkspaceBuildProgress
184+
<WorkspaceDeletedBanner
234185
workspace={workspace}
235-
transitionStats={transitionStats}
186+
handleClick={() => navigate(`/templates`)}
236187
/>
237-
)}
238188

239-
{typeof resources !== "undefined" && resources.length > 0 && (
240-
<Resources
241-
resources={resources}
242-
agentRow={(agent) => (
243-
<AgentRow
244-
key={agent.id}
245-
agent={agent}
246-
workspace={workspace}
247-
sshPrefix={sshPrefix}
248-
showApps={canUpdateWorkspace}
249-
hideSSHButton={hideSSHButton}
250-
hideVSCodeDesktopButton={hideVSCodeDesktopButton}
251-
serverVersion={serverVersion}
252-
onUpdateAgent={handleUpdate} // On updating the workspace the agent version is also updated
253-
/>
254-
)}
255-
/>
256-
)}
189+
{failedBuildLogs && (
190+
<Stack>
191+
<AlertBanner severity="error">
192+
<Stack
193+
className={styles.fullWidth}
194+
direction="row"
195+
alignItems="center"
196+
justifyContent="space-between"
197+
>
198+
<Stack spacing={0}>
199+
<span>Workspace build failed</span>
200+
<span className={styles.errorDetails}>
201+
{workspace.latest_build.job.error}
202+
</span>
203+
</Stack>
257204

258-
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
259-
<AlertBanner
260-
severity="error"
261-
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
262-
/>
263-
) : (
264-
<BuildsTable builds={builds} />
265-
)}
266-
</Stack>
267-
</Margins>
205+
{canUpdateTemplate && (
206+
<div>
207+
<Button
208+
onClick={handleBuildRetry}
209+
startIcon={<RefreshOutlined />}
210+
size="small"
211+
variant="outlined"
212+
>
213+
{t("actionButton.retryDebugMode")}
214+
</Button>
215+
</div>
216+
)}
217+
</Stack>
218+
</AlertBanner>
219+
<WorkspaceBuildLogs logs={failedBuildLogs} />
220+
</Stack>
221+
)}
222+
223+
{transitionStats !== undefined && (
224+
<WorkspaceBuildProgress
225+
workspace={workspace}
226+
transitionStats={transitionStats}
227+
/>
228+
)}
229+
230+
{typeof resources !== "undefined" && resources.length > 0 && (
231+
<Resources
232+
resources={resources}
233+
agentRow={(agent) => (
234+
<AgentRow
235+
key={agent.id}
236+
agent={agent}
237+
workspace={workspace}
238+
sshPrefix={sshPrefix}
239+
showApps={canUpdateWorkspace}
240+
hideSSHButton={hideSSHButton}
241+
hideVSCodeDesktopButton={hideVSCodeDesktopButton}
242+
serverVersion={serverVersion}
243+
onUpdateAgent={handleUpdate} // On updating the workspace the agent version is also updated
244+
/>
245+
)}
246+
/>
247+
)}
248+
249+
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
250+
<AlertBanner
251+
severity="error"
252+
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
253+
/>
254+
) : (
255+
<BuildsTable builds={builds} />
256+
)}
257+
</Stack>
258+
</Margins>
259+
</>
268260
)
269261
}
270262

271263
const spacerWidth = 300
272264

273265
export const useStyles = makeStyles((theme) => {
274266
return {
267+
content: {
268+
marginTop: theme.spacing(4),
269+
},
270+
275271
statusBadge: {
276272
marginLeft: theme.spacing(2),
277273
},

0 commit comments

Comments
 (0)