Skip to content

refactor: Refactor build page #4815

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 4 commits into from
Nov 1, 2022
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
18 changes: 13 additions & 5 deletions site/src/components/BuildsTable/BuildAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,26 @@ const StyledBadge = withStyles((theme) => ({
},
}))(Badge)

interface StyledAvatarProps {
size?: number
}

const StyledAvatar = withStyles((theme) => ({
root: {
background: theme.palette.divider,
color: theme.palette.text.primary,
border: `2px solid ${theme.palette.divider}`,
width: ({ size }: StyledAvatarProps) => size,
height: ({ size }: StyledAvatarProps) => size,

"& svg": {
width: 18,
height: 18,
width: ({ size }: StyledAvatarProps) => (size ? size / 2 : 18),
height: ({ size }: StyledAvatarProps) => (size ? size / 2 : 18),
},
},
}))(Avatar)

export type BuildAvatarProps = {
export interface BuildAvatarProps extends StyledAvatarProps {
build: WorkspaceBuild
}

Expand All @@ -48,7 +54,7 @@ const iconByTransition: Record<WorkspaceTransition, JSX.Element> = {
delete: <DeleteOutlined />,
}

export const BuildAvatar: FC<BuildAvatarProps> = ({ build }) => {
export const BuildAvatar: FC<BuildAvatarProps> = ({ build, size }) => {
const theme = useTheme<Theme>()
const displayBuildStatus = getDisplayWorkspaceBuildStatus(theme, build)

Expand All @@ -65,7 +71,9 @@ export const BuildAvatar: FC<BuildAvatarProps> = ({ build }) => {
}}
badgeContent={<div></div>}
>
<StyledAvatar>{iconByTransition[build.transition]}</StyledAvatar>
<StyledAvatar size={size}>
{iconByTransition[build.transition]}
</StyledAvatar>
</StyledBadge>
)
}
1 change: 1 addition & 0 deletions site/src/components/Logs/Logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ const useStyles = makeStyles((theme) => ({
userSelect: "none",
width: theme.spacing(12.5),
display: "inline-block",
color: theme.palette.text.secondary,
},
}))
67 changes: 67 additions & 0 deletions site/src/components/Stats/Stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { makeStyles } from "@material-ui/core/styles"
import { ComponentProps, FC, PropsWithChildren } from "react"

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

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

return (
<div className={styles.statItem}>
<span className={styles.statsLabel}>{label}:</span>
<span className={styles.statsValue}>{value}</span>
</div>
)
}

const useStyles = makeStyles((theme) => ({
stats: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
borderRadius: theme.shape.borderRadius,
border: `1px solid ${theme.palette.divider}`,
display: "flex",
alignItems: "center",
color: theme.palette.text.secondary,
margin: "0px",
[theme.breakpoints.down("sm")]: {
display: "block",
},
},

statItem: {
padding: theme.spacing(2),
paddingTop: theme.spacing(1.75),
display: "flex",
alignItems: "baseline",
gap: theme.spacing(1),
},

statsLabel: {
display: "block",
wordWrap: "break-word",
},

statsValue: {
marginTop: theme.spacing(0.25),
display: "block",
wordWrap: "break-word",
color: theme.palette.text.primary,

"& a": {
color: theme.palette.text.primary,
textDecoration: "none",
fontWeight: 600,

"&:hover": {
textDecoration: "underline",
},
},
},
}))
21 changes: 17 additions & 4 deletions site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { makeStyles } from "@material-ui/core/styles"
import dayjs from "dayjs"
import { FC } from "react"
import { FC, Fragment } from "react"
import { ProvisionerJobLog } from "../../api/typesGenerated"
import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"
import { Logs } from "../Logs/Logs"
Expand Down Expand Up @@ -59,7 +59,7 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({ logs }) => {
const shouldDisplayDuration = duration !== undefined

return (
<div key={stage}>
<Fragment key={stage}>
<div className={styles.header}>
<div>{stage}</div>
{shouldDisplayDuration && (
Expand All @@ -69,7 +69,7 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({ logs }) => {
)}
</div>
{!isEmpty && <Logs lines={lines} className={styles.codeBlock} />}
</div>
</Fragment>
)
})}
</div>
Expand All @@ -84,14 +84,27 @@ const useStyles = makeStyles((theme) => ({
},

header: {
fontSize: theme.typography.body1.fontSize,
fontSize: 14,
padding: theme.spacing(2),
paddingLeft: theme.spacing(4),
paddingRight: theme.spacing(4),
borderBottom: `1px solid ${theme.palette.divider}`,
backgroundColor: theme.palette.background.paper,
display: "flex",
alignItems: "center",
fontFamily: "Inter",

"&:first-child": {
borderTopLeftRadius: theme.shape.borderRadius,
borderTopRightRadius: theme.shape.borderRadius,
},

"&:last-child": {
borderBottom: 0,
borderTop: `1px solid ${theme.palette.divider}`,
borderBottomLeftRadius: theme.shape.borderRadius,
borderBottomRightRadius: theme.shape.borderRadius,
},
},

duration: {
Expand Down
143 changes: 24 additions & 119 deletions site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import Link from "@material-ui/core/Link"
import { makeStyles, useTheme } from "@material-ui/core/styles"
import { Stats, StatsItem } from "components/Stats/Stats"
import { FC } from "react"
import { Link as RouterLink } from "react-router-dom"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import { WorkspaceBuild } from "../../api/typesGenerated"
import { MONOSPACE_FONT_FAMILY } from "../../theme/constants"
import { combineClasses } from "../../util/combineClasses"
import {
displayWorkspaceBuildDuration,
getDisplayWorkspaceBuildInitiatedBy,
getDisplayWorkspaceBuildStatus,
} from "../../util/workspace"
import { displayWorkspaceBuildDuration } from "../../util/workspace"

export interface WorkspaceBuildStatsProps {
build: WorkspaceBuild
Expand All @@ -18,116 +12,27 @@ export interface WorkspaceBuildStatsProps {
export const WorkspaceBuildStats: FC<WorkspaceBuildStatsProps> = ({
build,
}) => {
const styles = useStyles()
const theme = useTheme()
const status = getDisplayWorkspaceBuildStatus(theme, build)
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(build)
const { t } = useTranslation("buildPage")

return (
<div className={styles.stats}>
<div className={styles.statItem}>
<span className={styles.statsLabel}>Workspace Name</span>
<Link
component={RouterLink}
to={`/@${build.workspace_owner_name}/${build.workspace_name}`}
className={combineClasses([styles.statsValue, styles.link])}
>
{build.workspace_name}
</Link>
</div>
<div className={styles.statsDivider} />

<div className={styles.statItem}>
<span className={styles.statsLabel}>Duration</span>
<span className={styles.statsValue}>
{displayWorkspaceBuildDuration(build)}
</span>
</div>
<div className={styles.statsDivider} />
<div className={styles.statItem}>
<span className={styles.statsLabel}>Started at</span>
<span className={styles.statsValue}>
{new Date(build.created_at).toLocaleString()}
</span>
</div>
<div className={styles.statsDivider} />
<div className={styles.statItem}>
<span className={styles.statsLabel}>Status</span>
<span className={styles.statsValue}>
<span style={{ color: status.color }}>{status.status}</span>
</span>
</div>
<div className={styles.statsDivider} />
<div className={styles.statItem}>
<span className={styles.statsLabel}>Action</span>
<span
className={combineClasses([styles.statsValue, styles.capitalize])}
>
{build.transition}
</span>
</div>
<div className={styles.statsDivider} />
<div className={styles.statItem}>
<span className={styles.statsLabel}>Initiated by</span>
<span className={styles.statsValue}>{initiatedBy}</span>
</div>
</div>
<Stats>
<StatsItem
label={t("stats.workspace")}
value={
<Link to={`/@${build.workspace_owner_name}/${build.workspace_name}`}>
{build.workspace_name}
</Link>
}
/>
<StatsItem
label={t("stats.duration")}
value={displayWorkspaceBuildDuration(build)}
/>
<StatsItem
label={t("stats.startedAt")}
value={new Date(build.created_at).toLocaleString()}
/>
<StatsItem label={t("stats.action")} value={build.transition} />
</Stats>
)
}

const useStyles = makeStyles((theme) => ({
stats: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
display: "flex",
alignItems: "center",
color: theme.palette.text.secondary,
fontFamily: MONOSPACE_FONT_FAMILY,
border: `1px solid ${theme.palette.divider}`,
[theme.breakpoints.down("sm")]: {
display: "block",
},
},

statItem: {
minWidth: "13%",
padding: theme.spacing(2),
paddingTop: theme.spacing(1.75),
},

statsLabel: {
fontSize: 12,
textTransform: "uppercase",
display: "block",
fontWeight: 600,
wordWrap: "break-word",
},

statsValue: {
fontSize: 16,
marginTop: theme.spacing(0.25),
display: "block",
wordWrap: "break-word",
},

statsDivider: {
width: 1,
height: theme.spacing(5),
backgroundColor: theme.palette.divider,
marginRight: theme.spacing(2),
[theme.breakpoints.down("sm")]: {
display: "none",
},
},

capitalize: {
textTransform: "capitalize",
},

link: {
color: theme.palette.text.primary,
fontWeight: 600,
},
}))
8 changes: 8 additions & 0 deletions site/src/i18n/en/buildPage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"stats": {
"workspace": "Workspace",
"duration": "Duration",
"startedAt": "Started at",
"action": "Action"
}
}
2 changes: 2 additions & 0 deletions site/src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import templatePage from "./templatePage.json"
import templatesPage from "./templatesPage.json"
import workspacePage from "./workspacePage.json"
import agent from "./agent.json"
import buildPage from "./buildPage.json"

export const en = {
common,
Expand All @@ -14,4 +15,5 @@ export const en = {
templatesPage,
createWorkspacePage,
agent,
buildPage,
}
20 changes: 16 additions & 4 deletions site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { BuildAvatar } from "components/BuildsTable/BuildAvatar"
import { FC } from "react"
import { ProvisionerJobLog, WorkspaceBuild } from "../../api/typesGenerated"
import { Loader } from "../../components/Loader/Loader"
import { Margins } from "../../components/Margins/Margins"
import {
PageHeader,
PageHeaderSubtitle,
PageHeaderTitle,
} from "../../components/PageHeader/PageHeader"
import { Stack } from "../../components/Stack/Stack"
Expand All @@ -29,11 +31,21 @@ export const WorkspaceBuildPageView: FC<WorkspaceBuildPageViewProps> = ({
}) => {
return (
<Margins>
<PageHeader>
<PageHeaderTitle>Logs</PageHeaderTitle>
</PageHeader>
{build && (
<PageHeader>
<Stack direction="row" alignItems="center" spacing={3}>
<BuildAvatar build={build} size={48} />
<div>
<PageHeaderTitle>Build #{build.build_number}</PageHeaderTitle>
<PageHeaderSubtitle condensed>
{build.initiator_name}
</PageHeaderSubtitle>
</div>
</Stack>
</PageHeader>
)}

<Stack>
<Stack spacing={4}>
{build &&
build.transition === "delete" &&
build.job.status === "failed" && (
Expand Down