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 1 commit
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
Next Next commit
refactor: Improve build page
  • Loading branch information
BrunoQuaresma committed Oct 31, 2022
commit 7f90d068b66d010dadefb7b2f1c819dde643446c
13 changes: 9 additions & 4 deletions site/src/components/BuildsTable/BuildAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,19 @@ const StyledAvatar = withStyles((theme) => ({
background: theme.palette.divider,
color: theme.palette.text.primary,
border: `2px solid ${theme.palette.divider}`,
width: ({ size }: { size?: number }) => size,
height: ({ size }: { size?: number }) => size,

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

export type BuildAvatarProps = {
build: WorkspaceBuild
size?: number
}

const iconByTransition: Record<WorkspaceTransition, JSX.Element> = {
Expand All @@ -48,7 +51,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 +68,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
142 changes: 22 additions & 120 deletions site/src/components/WorkspaceBuildStats/WorkspaceBuildStats.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
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 { 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 +11,25 @@ export interface WorkspaceBuildStatsProps {
export const WorkspaceBuildStats: FC<WorkspaceBuildStatsProps> = ({
build,
}) => {
const styles = useStyles()
const theme = useTheme()
const status = getDisplayWorkspaceBuildStatus(theme, build)
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(build)

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="Workspace"
value={
<Link to={`/@${build.workspace_owner_name}/${build.workspace_name}`}>
{build.workspace_name}
</Link>
}
/>
<StatsItem
label="Duration"
value={displayWorkspaceBuildDuration(build)}
/>
<StatsItem
label="Started at"
value={new Date(build.created_at).toLocaleString()}
/>
<StatsItem label="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,
},
}))
30 changes: 26 additions & 4 deletions site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { BuildAvatar } from "components/BuildsTable/BuildAvatar"
import { FC } from "react"
import { useParams } from "react-router-dom"
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 @@ -18,6 +21,13 @@ const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => {
)
}

const useBuildNumber = () => {
const { buildNumber } = useParams()
if (!buildNumber) {
throw new Error("Build number not found")
}
return buildNumber
}
export interface WorkspaceBuildPageViewProps {
logs: ProvisionerJobLog[] | undefined
build: WorkspaceBuild | undefined
Expand All @@ -27,13 +37,25 @@ export const WorkspaceBuildPageView: FC<WorkspaceBuildPageViewProps> = ({
logs,
build,
}) => {
const buildNumber = useBuildNumber()

return (
<Margins>
<PageHeader>
<PageHeaderTitle>Logs</PageHeaderTitle>
</PageHeader>
{build && (
<PageHeader>
<Stack direction="row" alignItems="center" spacing={3}>
<BuildAvatar build={build} size={48} />
<div>
<PageHeaderTitle>Build #{buildNumber}</PageHeaderTitle>
<PageHeaderSubtitle condensed>
{build.initiator_name}
</PageHeaderSubtitle>
</div>
</Stack>
</PageHeader>
)}

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