mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: show last build initiator for workspaces (#2921)
This commit is contained in:
@@ -6,7 +6,7 @@ import { Link as RouterLink } from "react-router-dom"
|
||||
import { Workspace } from "../../api/typesGenerated"
|
||||
import { CardRadius, MONOSPACE_FONT_FAMILY } from "../../theme/constants"
|
||||
import { combineClasses } from "../../util/combineClasses"
|
||||
import { getDisplayStatus } from "../../util/workspace"
|
||||
import { getDisplayStatus, getDisplayWorkspaceBuildInitiatedBy } from "../../util/workspace"
|
||||
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
|
||||
|
||||
const Language = {
|
||||
@@ -17,6 +17,7 @@ const Language = {
|
||||
lastBuiltLabel: "Last Built",
|
||||
outdated: "Outdated",
|
||||
upToDate: "Up to date",
|
||||
byLabel: "Last Built by",
|
||||
}
|
||||
|
||||
export interface WorkspaceStatsProps {
|
||||
@@ -27,6 +28,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({ workspace }) => {
|
||||
const styles = useStyles()
|
||||
const theme = useTheme()
|
||||
const status = getDisplayStatus(theme, workspace.latest_build)
|
||||
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(theme, workspace.latest_build)
|
||||
|
||||
return (
|
||||
<WorkspaceSection title={Language.workspaceDetails} contentsProps={{ className: styles.stats }}>
|
||||
@@ -59,6 +61,13 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({ workspace }) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.statsDivider} />
|
||||
<div className={styles.statItem}>
|
||||
<span className={styles.statsLabel}>{Language.byLabel}</span>
|
||||
<span className={styles.statsValue}>
|
||||
<span style={{ color: initiatedBy.color }}>{initiatedBy.initiatedBy}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.statsDivider} />
|
||||
<div className={styles.statItem}>
|
||||
<span className={styles.statsLabel}>{Language.statusLabel}</span>
|
||||
<span className={styles.statsValue}>
|
||||
@@ -88,7 +97,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
|
||||
statItem: {
|
||||
minWidth: "20%",
|
||||
minWidth: "16%",
|
||||
padding: theme.spacing(2),
|
||||
paddingTop: theme.spacing(1.75),
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ import dayjs from "dayjs"
|
||||
import relativeTime from "dayjs/plugin/relativeTime"
|
||||
import { FC } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { getDisplayStatus } from "../../util/workspace"
|
||||
import { getDisplayStatus, getDisplayWorkspaceBuildInitiatedBy } from "../../util/workspace"
|
||||
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
|
||||
import { AvatarData } from "../AvatarData/AvatarData"
|
||||
import { TableCellLink } from "../TableCellLink/TableCellLink"
|
||||
@@ -27,6 +27,7 @@ export const WorkspacesRow: FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ w
|
||||
const [workspaceState, send] = useActor(workspaceRef)
|
||||
const { data: workspace } = workspaceState.context
|
||||
const status = getDisplayStatus(theme, workspace.latest_build)
|
||||
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(theme, workspace.latest_build)
|
||||
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`
|
||||
|
||||
return (
|
||||
@@ -64,6 +65,9 @@ export const WorkspacesRow: FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ w
|
||||
{dayjs().to(dayjs(workspace.latest_build.created_at))}
|
||||
</span>
|
||||
</TableCellLink>
|
||||
<TableCellLink to={workspacePageLink}>
|
||||
<span style={{ color: initiatedBy.color }}>{initiatedBy.initiatedBy}</span>
|
||||
</TableCellLink>
|
||||
<TableCellLink to={workspacePageLink}>
|
||||
<span style={{ color: status.color }}>{status.status}</span>
|
||||
</TableCellLink>
|
||||
|
||||
@@ -13,6 +13,7 @@ const Language = {
|
||||
version: "Version",
|
||||
lastBuilt: "Last Built",
|
||||
status: "Status",
|
||||
lastBuiltBy: "By",
|
||||
}
|
||||
|
||||
export interface WorkspacesTableProps {
|
||||
@@ -26,10 +27,11 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({ isLoading, workspace
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell width="35%">{Language.name}</TableCell>
|
||||
<TableCell width="30%">{Language.name}</TableCell>
|
||||
<TableCell width="15%">{Language.template}</TableCell>
|
||||
<TableCell width="15%">{Language.version}</TableCell>
|
||||
<TableCell width="20%">{Language.lastBuilt}</TableCell>
|
||||
<TableCell width="10%">{Language.version}</TableCell>
|
||||
<TableCell width="15%">{Language.lastBuilt}</TableCell>
|
||||
<TableCell width="15%">{Language.lastBuiltBy}</TableCell>
|
||||
<TableCell width="15%">{Language.status}</TableCell>
|
||||
<TableCell width="1%"></TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import dayjs from "dayjs"
|
||||
import * as TypesGen from "../api/typesGenerated"
|
||||
import * as Mocks from "../testHelpers/entities"
|
||||
import { defaultWorkspaceExtension, isWorkspaceDeleted, isWorkspaceOn } from "./workspace"
|
||||
import { dark } from "../theme/theme"
|
||||
import {
|
||||
defaultWorkspaceExtension,
|
||||
getDisplayWorkspaceBuildInitiatedBy,
|
||||
isWorkspaceDeleted,
|
||||
isWorkspaceOn,
|
||||
} from "./workspace"
|
||||
|
||||
describe("util > workspace", () => {
|
||||
describe("isWorkspaceOn", () => {
|
||||
@@ -101,4 +107,34 @@ describe("util > workspace", () => {
|
||||
expect(defaultWorkspaceExtension(dayjs(startTime))).toEqual(request)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getDisplayWorkspaceBuildInitiatedBy", () => {
|
||||
it.each<[TypesGen.WorkspaceBuild, string, string]>([
|
||||
[Mocks.MockWorkspaceBuild, "#C1C1C1", "TestUser"],
|
||||
[
|
||||
{
|
||||
...Mocks.MockWorkspaceBuild,
|
||||
reason: "autostart",
|
||||
},
|
||||
"#7057FF",
|
||||
"system/autostart",
|
||||
],
|
||||
[
|
||||
{
|
||||
...Mocks.MockWorkspaceBuild,
|
||||
reason: "autostop",
|
||||
},
|
||||
"#7057FF",
|
||||
"system/autostop",
|
||||
],
|
||||
])(
|
||||
`getDisplayWorkspaceBuildInitiatedBy(%p) returns color: %p, initiatedBy: %p`,
|
||||
(build, color, initiatedBy) => {
|
||||
expect(getDisplayWorkspaceBuildInitiatedBy(dark, build)).toEqual({
|
||||
color: color,
|
||||
initiatedBy: initiatedBy,
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user