Skip to content

Commit 3e08bb4

Browse files
feat: Redesign build logs (#4734)
1 parent 6449443 commit 3e08bb4

File tree

10 files changed

+329
-123
lines changed

10 files changed

+329
-123
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import Avatar from "@material-ui/core/Avatar"
2+
import Badge from "@material-ui/core/Badge"
3+
import { Theme, useTheme, withStyles } from "@material-ui/core/styles"
4+
import { FC } from "react"
5+
import PlayArrowOutlined from "@material-ui/icons/PlayArrowOutlined"
6+
import PauseOutlined from "@material-ui/icons/PauseOutlined"
7+
import DeleteOutlined from "@material-ui/icons/DeleteOutlined"
8+
import { WorkspaceBuild, WorkspaceTransition } from "api/typesGenerated"
9+
import { getDisplayWorkspaceBuildStatus } from "util/workspace"
10+
import { PaletteIndex } from "theme/palettes"
11+
12+
interface StylesBadgeProps {
13+
type: PaletteIndex
14+
}
15+
16+
const StyledBadge = withStyles((theme) => ({
17+
badge: {
18+
backgroundColor: ({ type }: StylesBadgeProps) => theme.palette[type].light,
19+
borderRadius: "100%",
20+
width: 8,
21+
minWidth: 8,
22+
height: 8,
23+
display: "block",
24+
padding: 0,
25+
},
26+
}))(Badge)
27+
28+
const StyledAvatar = withStyles((theme) => ({
29+
root: {
30+
background: theme.palette.background.paperLight,
31+
color: theme.palette.text.primary,
32+
border: `2px solid ${theme.palette.divider}`,
33+
34+
"& svg": {
35+
width: 24,
36+
height: 24,
37+
},
38+
},
39+
}))(Avatar)
40+
41+
export type BuildAvatarProps = {
42+
build: WorkspaceBuild
43+
}
44+
45+
const iconByTransition: Record<WorkspaceTransition, JSX.Element> = {
46+
start: <PlayArrowOutlined />,
47+
stop: <PauseOutlined />,
48+
delete: <DeleteOutlined />,
49+
}
50+
51+
export const BuildAvatar: FC<BuildAvatarProps> = ({ build }) => {
52+
const theme = useTheme<Theme>()
53+
const displayBuildStatus = getDisplayWorkspaceBuildStatus(theme, build)
54+
55+
return (
56+
<StyledBadge
57+
role="status"
58+
type={displayBuildStatus.type}
59+
arial-label={displayBuildStatus.status}
60+
title={displayBuildStatus.status}
61+
overlap="circular"
62+
anchorOrigin={{
63+
vertical: "bottom",
64+
horizontal: "right",
65+
}}
66+
badgeContent={<div></div>}
67+
>
68+
<StyledAvatar>{iconByTransition[build.transition]}</StyledAvatar>
69+
</StyledBadge>
70+
)
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import TableCell from "@material-ui/core/TableCell"
3+
import TableRow from "@material-ui/core/TableRow"
4+
import formatRelative from "date-fns/formatRelative"
5+
import { FC } from "react"
6+
7+
export interface BuildDateRow {
8+
date: Date
9+
}
10+
11+
export const BuildDateRow: FC<BuildDateRow> = ({ date }) => {
12+
const styles = useStyles()
13+
// We only want the message related to the date since the time is displayed
14+
// inside of the build row
15+
const displayDate = formatRelative(date, new Date()).split("at")[0]
16+
17+
return (
18+
<TableRow className={styles.buildDateRow}>
19+
<TableCell
20+
className={styles.buildDateCell}
21+
title={date.toLocaleDateString()}
22+
>
23+
{displayDate}
24+
</TableCell>
25+
</TableRow>
26+
)
27+
}
28+
29+
const useStyles = makeStyles((theme) => ({
30+
buildDateRow: {
31+
background: theme.palette.background.paper,
32+
33+
"&:not(:first-child) td": {
34+
borderTop: `1px solid ${theme.palette.divider}`,
35+
},
36+
},
37+
38+
buildDateCell: {
39+
padding: `${theme.spacing(1, 4)} !important`,
40+
background: `${theme.palette.background.paperLight} !important`,
41+
fontSize: 12,
42+
position: "relative",
43+
color: theme.palette.text.secondary,
44+
textTransform: "capitalize",
45+
},
46+
}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import TableCell from "@material-ui/core/TableCell"
3+
import TableRow from "@material-ui/core/TableRow"
4+
import { WorkspaceBuild } from "api/typesGenerated"
5+
import { Stack } from "components/Stack/Stack"
6+
import { useClickable } from "hooks/useClickable"
7+
import { useTranslation } from "react-i18next"
8+
import { useNavigate } from "react-router-dom"
9+
import { MONOSPACE_FONT_FAMILY } from "theme/constants"
10+
import {
11+
displayWorkspaceBuildDuration,
12+
getDisplayWorkspaceBuildInitiatedBy,
13+
} from "util/workspace"
14+
import { BuildAvatar } from "./BuildAvatar"
15+
16+
export interface BuildRowProps {
17+
build: WorkspaceBuild
18+
}
19+
20+
export const BuildRow: React.FC<BuildRowProps> = ({ build }) => {
21+
const styles = useStyles()
22+
const { t } = useTranslation("workspacePage")
23+
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(build)
24+
const navigate = useNavigate()
25+
const clickableProps = useClickable(() =>
26+
navigate(`builds/${build.build_number}`),
27+
)
28+
29+
return (
30+
<TableRow
31+
hover
32+
data-testid={`build-${build.id}`}
33+
className={styles.buildRow}
34+
{...clickableProps}
35+
>
36+
<TableCell className={styles.buildCell}>
37+
<Stack
38+
direction="row"
39+
alignItems="center"
40+
className={styles.buildWrapper}
41+
>
42+
<Stack direction="row" alignItems="center">
43+
<BuildAvatar build={build} />
44+
<div>
45+
<Stack
46+
className={styles.buildResume}
47+
direction="row"
48+
alignItems="center"
49+
spacing={1}
50+
>
51+
<span>
52+
<strong>{initiatedBy}</strong>{" "}
53+
{build.reason !== "initiator"
54+
? t("buildMessage.automatically")
55+
: ""}
56+
<strong>{t(`buildMessage.${build.transition}`)}</strong>{" "}
57+
{t("buildMessage.theWorkspace")}
58+
</span>
59+
60+
<span className={styles.buildTime}>
61+
{new Date(build.created_at).toLocaleTimeString()}
62+
</span>
63+
</Stack>
64+
65+
<Stack direction="row" spacing={1}>
66+
<span className={styles.buildInfo}>
67+
{t("buildData.reason")}: <strong>{build.reason}</strong>
68+
</span>
69+
70+
<span className={styles.buildInfo}>
71+
{t("buildData.duration")}:{" "}
72+
<strong>{displayWorkspaceBuildDuration(build)}</strong>
73+
</span>
74+
</Stack>
75+
</div>
76+
</Stack>
77+
</Stack>
78+
</TableCell>
79+
</TableRow>
80+
)
81+
}
82+
83+
const useStyles = makeStyles((theme) => ({
84+
buildRow: {
85+
cursor: "pointer",
86+
87+
"&:focus": {
88+
outlineStyle: "solid",
89+
outlineOffset: -1,
90+
outlineWidth: 2,
91+
outlineColor: theme.palette.secondary.dark,
92+
},
93+
94+
"&:not(:last-child) td:before": {
95+
position: "absolute",
96+
top: 20,
97+
left: 50,
98+
display: "block",
99+
content: "''",
100+
height: "100%",
101+
width: 2,
102+
background: theme.palette.divider,
103+
},
104+
},
105+
106+
buildWrapper: {
107+
padding: theme.spacing(2, 4),
108+
},
109+
110+
buildCell: {
111+
padding: "0 !important",
112+
position: "relative",
113+
borderBottom: 0,
114+
},
115+
116+
buildResume: {
117+
...theme.typography.body1,
118+
fontFamily: "inherit",
119+
},
120+
121+
buildInfo: {
122+
...theme.typography.body2,
123+
fontSize: 12,
124+
fontFamily: "inherit",
125+
color: theme.palette.text.secondary,
126+
display: "block",
127+
},
128+
129+
buildTime: {
130+
color: theme.palette.text.secondary,
131+
fontSize: 12,
132+
},
133+
134+
buildRight: {
135+
width: "auto",
136+
},
137+
138+
buildExtraInfo: {
139+
...theme.typography.body2,
140+
fontFamily: MONOSPACE_FONT_FAMILY,
141+
color: theme.palette.text.secondary,
142+
whiteSpace: "nowrap",
143+
},
144+
}))

0 commit comments

Comments
 (0)