Skip to content

refactor(site): Update workspace header #7433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions site/src/components/PageHeader/FullWidthPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { makeStyles } from "@material-ui/core/styles"
import { FC, PropsWithChildren } from "react"

export const FullWidthPageHeader: FC<PropsWithChildren> = ({ children }) => {
const styles = useStyles()

return (
<header className={styles.header} data-testid="header">
{children}
</header>
)
}

export const PageHeaderActions: FC<PropsWithChildren> = ({ children }) => {
const styles = useStyles()
return <div className={styles.actions}>{children}</div>
}

export const PageHeaderTitle: FC<PropsWithChildren> = ({ children }) => {
const styles = useStyles()
return <h1 className={styles.title}>{children}</h1>
}

export const PageHeaderSubtitle: FC<PropsWithChildren> = ({ children }) => {
const styles = useStyles()
return <span className={styles.subtitle}>{children}</span>
}

const useStyles = makeStyles((theme) => ({
header: {
padding: theme.spacing(3),
background: theme.palette.background.paper,
borderBottom: `1px solid ${theme.palette.divider}`,
display: "flex",
alignItems: "center",
gap: theme.spacing(6),
position: "sticky",
top: 0,
zIndex: 10,
flexWrap: "wrap",

[theme.breakpoints.down("md")]: {
position: "unset",
alignItems: "flex-start",
},
[theme.breakpoints.down("sm")]: {
flexDirection: "column",
},
},
actions: {
marginLeft: "auto",
[theme.breakpoints.down("sm")]: {
marginLeft: "unset",
},
},
title: {
fontSize: 18,
fontWeight: 500,
margin: 0,
},
subtitle: {
fontSize: 14,
color: theme.palette.text.secondary,
marginTop: theme.spacing(0.25),
display: "block",
},
}))
25 changes: 18 additions & 7 deletions site/src/components/Stats/Stats.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import { makeStyles } from "@material-ui/core/styles"
import { ComponentProps, FC, PropsWithChildren } from "react"
import { combineClasses } from "utils/combineClasses"

export const Stats: FC<PropsWithChildren<ComponentProps<"div">>> = (props) => {
export const Stats: FC<ComponentProps<"div">> = (props) => {
const styles = useStyles()
return <div className={styles.stats} {...props} />
return (
<div
{...props}
className={combineClasses([styles.stats, props.className])}
/>
)
}

export const StatsItem: FC<{
label: string
value: string | number | JSX.Element
}> = ({ label, value }) => {
export const StatsItem: FC<
{
label: string
value: string | number | JSX.Element
} & ComponentProps<"div">
> = ({ label, value, ...divProps }) => {
const styles = useStyles()

return (
<div className={styles.statItem}>
<div
{...divProps}
className={combineClasses([styles.statItem, divProps.className])}
>
<span className={styles.statsLabel}>{label}:</span>
<span className={styles.statsValue}>{value}</span>
</div>
Expand Down
230 changes: 113 additions & 117 deletions site/src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ import {
ActiveTransition,
WorkspaceBuildProgress,
} from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
import { FC } from "react"
import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import * as TypesGen from "../../api/typesGenerated"
import { AlertBanner } from "../AlertBanner/AlertBanner"
import { BuildsTable } from "../BuildsTable/BuildsTable"
import { Margins } from "../Margins/Margins"
import {
PageHeader,
PageHeaderSubtitle,
PageHeaderTitle,
} from "../PageHeader/PageHeader"
import { Resources } from "../Resources/Resources"
import { Stack } from "../Stack/Stack"
import { WorkspaceActions } from "../WorkspaceActions/WorkspaceActions"
import { WorkspaceDeletedBanner } from "../WorkspaceDeletedBanner/WorkspaceDeletedBanner"
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
import {
FullWidthPageHeader,
PageHeaderActions,
PageHeaderTitle,
PageHeaderSubtitle,
} from "components/PageHeader/FullWidthPageHeader"

export enum WorkspaceErrors {
GET_BUILDS_ERROR = "getBuildsError",
Expand Down Expand Up @@ -125,64 +125,22 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
transitionStats = ActiveTransition(template, workspace)
}
return (
<Margins>
<PageHeader
actions={
<Stack direction="row" spacing={1} className={styles.actions}>
<WorkspaceActions
workspaceStatus={workspace.latest_build.status}
isOutdated={workspace.outdated}
handleStart={handleStart}
handleStop={handleStop}
handleRestart={handleRestart}
handleDelete={handleDelete}
handleUpdate={handleUpdate}
handleCancel={handleCancel}
handleSettings={handleSettings}
handleChangeVersion={handleChangeVersion}
canChangeVersions={canChangeVersions}
isUpdating={isUpdating}
isRestarting={isRestarting}
/>
</Stack>
}
>
<>
<FullWidthPageHeader>
<Stack direction="row" spacing={3} alignItems="center">
<Avatar
size="xl"
size="md"
src={workspace.template_icon}
variant={workspace.template_icon ? "square" : undefined}
fitImage={Boolean(workspace.template_icon)}
>
{workspace.name}
</Avatar>
<div>
<PageHeaderTitle>
{workspace.name}
<WorkspaceStatusBadge
build={workspace.latest_build}
className={styles.statusBadge}
/>
</PageHeaderTitle>
<PageHeaderSubtitle condensed>
{workspace.owner_name}
</PageHeaderSubtitle>
<PageHeaderTitle>{workspace.name}</PageHeaderTitle>
<PageHeaderSubtitle>{workspace.owner_name}</PageHeaderSubtitle>
</div>
</Stack>
</PageHeader>

<Stack
direction="column"
className={styles.firstColumnSpacer}
spacing={4}
>
{buildError}
{cancellationError}

<WorkspaceDeletedBanner
workspace={workspace}
handleClick={() => navigate(`/templates`)}
/>

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

{failedBuildLogs && (
<Stack>
<AlertBanner severity="error">
<Stack
className={styles.fullWidth}
direction="row"
alignItems="center"
justifyContent="space-between"
>
<Stack spacing={0}>
<span>Workspace build failed</span>
<span className={styles.errorDetails}>
{workspace.latest_build.job.error}
</span>
</Stack>
<PageHeaderActions>
<WorkspaceActions
workspaceStatus={workspace.latest_build.status}
isOutdated={workspace.outdated}
handleStart={handleStart}
handleStop={handleStop}
handleRestart={handleRestart}
handleDelete={handleDelete}
handleUpdate={handleUpdate}
handleCancel={handleCancel}
handleSettings={handleSettings}
handleChangeVersion={handleChangeVersion}
canChangeVersions={canChangeVersions}
isUpdating={isUpdating}
isRestarting={isRestarting}
/>
</PageHeaderActions>
</FullWidthPageHeader>

{canUpdateTemplate && (
<div>
<Button
onClick={handleBuildRetry}
startIcon={<RefreshOutlined />}
size="small"
variant="outlined"
>
{t("actionButton.retryDebugMode")}
</Button>
</div>
)}
</Stack>
</AlertBanner>
<WorkspaceBuildLogs logs={failedBuildLogs} />
</Stack>
)}
<Margins className={styles.content}>
<Stack
direction="column"
className={styles.firstColumnSpacer}
spacing={4}
>
{buildError}
{cancellationError}

{transitionStats !== undefined && (
<WorkspaceBuildProgress
<WorkspaceDeletedBanner
workspace={workspace}
transitionStats={transitionStats}
handleClick={() => navigate(`/templates`)}
/>
)}

{typeof resources !== "undefined" && resources.length > 0 && (
<Resources
resources={resources}
agentRow={(agent) => (
<AgentRow
key={agent.id}
agent={agent}
workspace={workspace}
sshPrefix={sshPrefix}
showApps={canUpdateWorkspace}
hideSSHButton={hideSSHButton}
hideVSCodeDesktopButton={hideVSCodeDesktopButton}
serverVersion={serverVersion}
onUpdateAgent={handleUpdate} // On updating the workspace the agent version is also updated
/>
)}
/>
)}
{failedBuildLogs && (
<Stack>
<AlertBanner severity="error">
<Stack
className={styles.fullWidth}
direction="row"
alignItems="center"
justifyContent="space-between"
>
<Stack spacing={0}>
<span>Workspace build failed</span>
<span className={styles.errorDetails}>
{workspace.latest_build.job.error}
</span>
</Stack>

{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
<AlertBanner
severity="error"
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
/>
) : (
<BuildsTable builds={builds} />
)}
</Stack>
</Margins>
{canUpdateTemplate && (
<div>
<Button
onClick={handleBuildRetry}
startIcon={<RefreshOutlined />}
size="small"
variant="outlined"
>
{t("actionButton.retryDebugMode")}
</Button>
</div>
)}
</Stack>
</AlertBanner>
<WorkspaceBuildLogs logs={failedBuildLogs} />
</Stack>
)}

{transitionStats !== undefined && (
<WorkspaceBuildProgress
workspace={workspace}
transitionStats={transitionStats}
/>
)}

{typeof resources !== "undefined" && resources.length > 0 && (
<Resources
resources={resources}
agentRow={(agent) => (
<AgentRow
key={agent.id}
agent={agent}
workspace={workspace}
sshPrefix={sshPrefix}
showApps={canUpdateWorkspace}
hideSSHButton={hideSSHButton}
hideVSCodeDesktopButton={hideVSCodeDesktopButton}
serverVersion={serverVersion}
onUpdateAgent={handleUpdate} // On updating the workspace the agent version is also updated
/>
)}
/>
)}

{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
<AlertBanner
severity="error"
error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]}
/>
) : (
<BuildsTable builds={builds} />
)}
</Stack>
</Margins>
</>
)
}

const spacerWidth = 300

export const useStyles = makeStyles((theme) => {
return {
content: {
marginTop: theme.spacing(4),
},

statusBadge: {
marginLeft: theme.spacing(2),
},
Expand Down
Loading