refactor: Refactor build page (#4815)

* refactor: Improve build page

* Add build translation

* Add build avatar better props

* Get build number from build
This commit is contained in:
Bruno Quaresma
2022-11-01 11:49:32 -03:00
committed by GitHub
parent cf794f1046
commit a7e5588a65
8 changed files with 148 additions and 132 deletions
@@ -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
}
@@ -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)
@@ -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
View File
@@ -57,5 +57,6 @@ const useStyles = makeStyles((theme) => ({
userSelect: "none",
width: theme.spacing(12.5),
display: "inline-block",
color: theme.palette.text.secondary,
},
}))
+67
View File
@@ -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",
},
},
},
}))
@@ -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"
@@ -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 && (
@@ -69,7 +69,7 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({ logs }) => {
)}
</div>
{!isEmpty && <Logs lines={lines} className={styles.codeBlock} />}
</div>
</Fragment>
)
})}
</div>
@@ -84,7 +84,7 @@ const useStyles = makeStyles((theme) => ({
},
header: {
fontSize: theme.typography.body1.fontSize,
fontSize: 14,
padding: theme.spacing(2),
paddingLeft: theme.spacing(4),
paddingRight: theme.spacing(4),
@@ -92,6 +92,19 @@ const useStyles = makeStyles((theme) => ({
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: {
@@ -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
@@ -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
View File
@@ -0,0 +1,8 @@
{
"stats": {
"workspace": "Workspace",
"duration": "Duration",
"startedAt": "Started at",
"action": "Action"
}
}
+2
View File
@@ -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,
@@ -14,4 +15,5 @@ export const en = {
templatesPage,
createWorkspacePage,
agent,
buildPage,
}
@@ -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"
@@ -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" && (