refactor: Show template version in the workspace page (#5194)

This commit is contained in:
Bruno Quaresma
2022-11-30 11:13:07 -03:00
committed by GitHub
parent 5817d2a301
commit 41f10e7b69
8 changed files with 158 additions and 175 deletions

View File

@ -34,9 +34,18 @@ export const BuildRow: React.FC<BuildRowProps> = ({ build }) => {
alignItems="center" alignItems="center"
className={styles.buildWrapper} className={styles.buildWrapper}
> >
<Stack direction="row" alignItems="center"> <Stack
direction="row"
alignItems="center"
className={styles.fullWidth}
>
<BuildAvatar build={build} /> <BuildAvatar build={build} />
<div> <Stack
direction="row"
justifyContent="space-between"
alignItems="center"
className={styles.fullWidth}
>
<Stack <Stack
className={styles.buildSummary} className={styles.buildSummary}
direction="row" direction="row"
@ -66,8 +75,13 @@ export const BuildRow: React.FC<BuildRowProps> = ({ build }) => {
{t("buildData.duration")}:{" "} {t("buildData.duration")}:{" "}
<strong>{displayWorkspaceBuildDuration(build)}</strong> <strong>{displayWorkspaceBuildDuration(build)}</strong>
</span> </span>
<span className={styles.buildInfo}>
{t("buildData.version")}:{" "}
<strong>{build.template_version_name}</strong>
</span>
</Stack> </Stack>
</div> </Stack>
</Stack> </Stack>
</Stack> </Stack>
</TableCell> </TableCell>
@ -114,4 +128,8 @@ const useStyles = makeStyles((theme) => ({
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
whiteSpace: "nowrap", whiteSpace: "nowrap",
}, },
fullWidth: {
width: "100%",
},
})) }))

View File

@ -30,17 +30,24 @@ const useStyles = makeStyles((theme) => ({
alignItems: "center", alignItems: "center",
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
margin: "0px", margin: "0px",
[theme.breakpoints.down("sm")]: { [theme.breakpoints.down("sm")]: {
display: "block", display: "block",
padding: theme.spacing(2),
}, },
}, },
statItem: { statItem: {
padding: theme.spacing(2), padding: theme.spacing(1.75),
paddingTop: theme.spacing(1.75), paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
display: "flex", display: "flex",
alignItems: "baseline", alignItems: "baseline",
gap: theme.spacing(1), gap: theme.spacing(1),
[theme.breakpoints.down("sm")]: {
padding: theme.spacing(1),
},
}, },
statsLabel: { statsLabel: {
@ -50,9 +57,10 @@ const useStyles = makeStyles((theme) => ({
statsValue: { statsValue: {
marginTop: theme.spacing(0.25), marginTop: theme.spacing(0.25),
display: "block", display: "flex",
wordWrap: "break-word", wordWrap: "break-word",
color: theme.palette.text.primary, color: theme.palette.text.primary,
alignItems: "center",
"& a": { "& a": {
color: theme.palette.text.primary, color: theme.palette.text.primary,

View File

@ -4,6 +4,7 @@ import { makeStyles } from "@material-ui/core/styles"
import HelpIcon from "@material-ui/icons/HelpOutline" import HelpIcon from "@material-ui/icons/HelpOutline"
import OpenInNewIcon from "@material-ui/icons/OpenInNew" import OpenInNewIcon from "@material-ui/icons/OpenInNew"
import React, { createContext, useContext, useRef, useState } from "react" import React, { createContext, useContext, useRef, useState } from "react"
import { combineClasses } from "util/combineClasses"
import { Stack } from "../../Stack/Stack" import { Stack } from "../../Stack/Stack"
type Icon = typeof HelpIcon type Icon = typeof HelpIcon
@ -13,6 +14,9 @@ export interface HelpTooltipProps {
// Useful to test on storybook // Useful to test on storybook
open?: boolean open?: boolean
size?: Size size?: Size
icon?: Icon
iconClassName?: string
buttonClassName?: string
} }
export const Language = { export const Language = {
@ -66,7 +70,14 @@ export const HelpPopover: React.FC<
export const HelpTooltip: React.FC< export const HelpTooltip: React.FC<
React.PropsWithChildren<HelpTooltipProps> React.PropsWithChildren<HelpTooltipProps>
> = ({ children, open, size = "medium" }) => { > = ({
children,
open,
size = "medium",
icon: Icon = HelpIcon,
iconClassName,
buttonClassName,
}) => {
const styles = useStyles({ size }) const styles = useStyles({ size })
const anchorRef = useRef<HTMLButtonElement>(null) const anchorRef = useRef<HTMLButtonElement>(null)
const [isOpen, setIsOpen] = useState(Boolean(open)) const [isOpen, setIsOpen] = useState(Boolean(open))
@ -81,7 +92,7 @@ export const HelpTooltip: React.FC<
<button <button
ref={anchorRef} ref={anchorRef}
aria-describedby={id} aria-describedby={id}
className={styles.button} className={combineClasses([styles.button, buttonClassName])}
onClick={(event) => { onClick={(event) => {
event.stopPropagation() event.stopPropagation()
setIsOpen(true) setIsOpen(true)
@ -94,7 +105,7 @@ export const HelpTooltip: React.FC<
}} }}
aria-label={Language.ariaLabel} aria-label={Language.ariaLabel}
> >
<HelpIcon className={styles.icon} /> <Icon className={combineClasses([styles.icon, iconClassName])} />
</button> </button>
<HelpPopover <HelpPopover
id={id} id={id}

View File

@ -7,6 +7,9 @@ import {
HelpTooltipText, HelpTooltipText,
HelpTooltipTitle, HelpTooltipTitle,
} from "./HelpTooltip" } from "./HelpTooltip"
import InfoIcon from "@material-ui/icons/InfoOutlined"
import { makeStyles } from "@material-ui/core/styles"
import { colors } from "theme/colors"
export const Language = { export const Language = {
outdatedLabel: "Outdated", outdatedLabel: "Outdated",
@ -24,8 +27,15 @@ export const OutdatedHelpTooltip: FC<React.PropsWithChildren<TooltipProps>> = ({
onUpdateVersion, onUpdateVersion,
ariaLabel, ariaLabel,
}) => { }) => {
const styles = useStyles()
return ( return (
<HelpTooltip size="small"> <HelpTooltip
size="small"
icon={InfoIcon}
iconClassName={styles.icon}
buttonClassName={styles.button}
>
<HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle> <HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle>
<HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText> <HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup> <HelpTooltipLinksGroup>
@ -40,3 +50,17 @@ export const OutdatedHelpTooltip: FC<React.PropsWithChildren<TooltipProps>> = ({
</HelpTooltip> </HelpTooltip>
) )
} }
const useStyles = makeStyles(() => ({
icon: {
color: colors.yellow[5],
},
button: {
opacity: 1,
"&:hover": {
opacity: 1,
},
},
}))

View File

@ -1,15 +1,14 @@
import Link from "@material-ui/core/Link" import Link from "@material-ui/core/Link"
import { makeStyles } from "@material-ui/core/styles"
import { OutdatedHelpTooltip } from "components/Tooltips" import { OutdatedHelpTooltip } from "components/Tooltips"
import { FC } from "react" import { FC } from "react"
import { Link as RouterLink } from "react-router-dom" import { Link as RouterLink } from "react-router-dom"
import { combineClasses } from "util/combineClasses"
import { createDayString } from "util/createDayString" import { createDayString } from "util/createDayString"
import { import {
getDisplayWorkspaceBuildInitiatedBy, getDisplayWorkspaceBuildInitiatedBy,
getDisplayWorkspaceTemplateName, getDisplayWorkspaceTemplateName,
} from "util/workspace" } from "util/workspace"
import { Workspace } from "../../api/typesGenerated" import { Workspace } from "../../api/typesGenerated"
import { Stats, StatsItem } from "components/Stats/Stats"
const Language = { const Language = {
workspaceDetails: "Workspace Details", workspaceDetails: "Workspace Details",
@ -34,110 +33,57 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
quota_budget, quota_budget,
handleUpdate, handleUpdate,
}) => { }) => {
const styles = useStyles()
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy( const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(
workspace.latest_build, workspace.latest_build,
) )
const displayTemplateName = getDisplayWorkspaceTemplateName(workspace) const displayTemplateName = getDisplayWorkspaceTemplateName(workspace)
return ( return (
<div className={styles.stats} aria-label={Language.workspaceDetails}> <Stats aria-label={Language.workspaceDetails}>
<div className={styles.statItem}> <StatsItem
<span className={styles.statsLabel}>{Language.templateLabel}:</span> label={Language.templateLabel}
<Link value={
component={RouterLink} <Link
to={`/templates/${workspace.template_name}`} component={RouterLink}
className={combineClasses([styles.statsValue, styles.link])} to={`/templates/${workspace.template_name}`}
> >
{displayTemplateName} {displayTemplateName}
</Link> </Link>
</div> }
<div className={styles.statItem}> />
<span className={styles.statsLabel}>{Language.versionLabel}:</span> <StatsItem
<span className={styles.statsValue}> label={Language.versionLabel}
{workspace.outdated ? ( value={
<span className={styles.outdatedLabel}> <>
{Language.outdated} <Link
component={RouterLink}
to={`/templates/${workspace.template_name}/versions/${workspace.latest_build.template_version_name}`}
>
{workspace.latest_build.template_version_name}
</Link>
{workspace.outdated && (
<OutdatedHelpTooltip <OutdatedHelpTooltip
onUpdateVersion={handleUpdate} onUpdateVersion={handleUpdate}
ariaLabel="update version" ariaLabel="update version"
/> />
</span> )}
) : ( </>
Language.upToDate }
)} />
</span> <StatsItem
</div> label={Language.lastBuiltLabel}
<div className={styles.statItem}> value={createDayString(workspace.latest_build.created_at)}
<span className={styles.statsLabel}>{Language.lastBuiltLabel}:</span> />
<span className={styles.statsValue} data-chromatic="ignore"> <StatsItem label={Language.byLabel} value={initiatedBy} />
{createDayString(workspace.latest_build.created_at)}
</span>
</div>
<div className={styles.statItem}>
<span className={styles.statsLabel}>{Language.byLabel}:</span>
<span className={styles.statsValue}>{initiatedBy}</span>
</div>
{workspace.latest_build.daily_cost > 0 && ( {workspace.latest_build.daily_cost > 0 && (
<div className={styles.statItem}> <StatsItem
<span className={styles.statsLabel}>{Language.costLabel}:</span> label={Language.costLabel}
<span className={styles.statsValue}> value={`${workspace.latest_build.daily_cost} ${
{workspace.latest_build.daily_cost} / {quota_budget} quota_budget ? `/ ${quota_budget}` : ""
</span> }`}
</div> />
)} )}
</div> </Stats>
) )
} }
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,
},
capitalize: {
textTransform: "capitalize",
},
link: {
color: theme.palette.text.primary,
fontWeight: 600,
},
outdatedLabel: {
color: theme.palette.error.main,
display: "flex",
alignItems: "center",
gap: theme.spacing(0.5),
},
}))

View File

@ -1,53 +1,41 @@
import { makeStyles, Theme } from "@material-ui/core/styles" import TableCell from "@material-ui/core/TableCell"
import { makeStyles } from "@material-ui/core/styles"
import TableRow from "@material-ui/core/TableRow" import TableRow from "@material-ui/core/TableRow"
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight" import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
import useTheme from "@material-ui/styles/useTheme"
import { useActor } from "@xstate/react" import { useActor } from "@xstate/react"
import { AvatarData } from "components/AvatarData/AvatarData" import { AvatarData } from "components/AvatarData/AvatarData"
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge" import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
import { useClickable } from "hooks/useClickable"
import { FC } from "react" import { FC } from "react"
import { useNavigate } from "react-router-dom" import { useNavigate } from "react-router-dom"
import { getDisplayWorkspaceTemplateName } from "util/workspace" import { getDisplayWorkspaceTemplateName } from "util/workspace"
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService" import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
import { LastUsed } from "../LastUsed/LastUsed" import { LastUsed } from "../LastUsed/LastUsed"
import {
TableCellData,
TableCellDataPrimary,
} from "../TableCellData/TableCellData"
import { TableCellLink } from "../TableCellLink/TableCellLink"
import { OutdatedHelpTooltip } from "../Tooltips" import { OutdatedHelpTooltip } from "../Tooltips"
const Language = { export const WorkspacesRow: FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({
upToDateLabel: "Up to date", workspaceRef,
outdatedLabel: "Outdated", }) => {
}
export const WorkspacesRow: FC<
React.PropsWithChildren<{ workspaceRef: WorkspaceItemMachineRef }>
> = ({ workspaceRef }) => {
const styles = useStyles() const styles = useStyles()
const navigate = useNavigate() const navigate = useNavigate()
const theme: Theme = useTheme()
const [workspaceState, send] = useActor(workspaceRef) const [workspaceState, send] = useActor(workspaceRef)
const { data: workspace } = workspaceState.context const { data: workspace } = workspaceState.context
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}` const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`
const hasTemplateIcon = const hasTemplateIcon =
workspace.template_icon && workspace.template_icon !== "" workspace.template_icon && workspace.template_icon !== ""
const displayTemplateName = getDisplayWorkspaceTemplateName(workspace) const displayTemplateName = getDisplayWorkspaceTemplateName(workspace)
const clickable = useClickable(() => {
navigate(workspacePageLink)
})
return ( return (
<TableRow <TableRow
className={styles.row}
hover hover
data-testid={`workspace-${workspace.id}`} data-testid={`workspace-${workspace.id}`}
tabIndex={0} {...clickable}
onKeyDown={(event) => {
if (event.key === "Enter") {
navigate(workspacePageLink)
}
}}
className={styles.clickableTableRow}
> >
<TableCellLink to={workspacePageLink}> <TableCell>
<AvatarData <AvatarData
highlightTitle highlightTitle
title={workspace.name} title={workspace.name}
@ -60,78 +48,61 @@ export const WorkspacesRow: FC<
) : undefined ) : undefined
} }
/> />
</TableCellLink> </TableCell>
<TableCellLink to={workspacePageLink}> <TableCell>{displayTemplateName}</TableCell>
<TableCellDataPrimary>{displayTemplateName}</TableCellDataPrimary>
</TableCellLink>
<TableCellLink to={workspacePageLink}>
<TableCellData>
<LastUsed lastUsedAt={workspace.last_used_at} />
</TableCellData>
</TableCellLink>
<TableCellLink to={workspacePageLink}> <TableCell>
{workspace.outdated ? ( <div className={styles.version}>
<span className={styles.outdatedLabel}> {workspace.latest_build.template_version_name}
{Language.outdatedLabel} {workspace.outdated && (
<OutdatedHelpTooltip <OutdatedHelpTooltip
onUpdateVersion={() => { onUpdateVersion={() => {
send("UPDATE_VERSION") send("UPDATE_VERSION")
}} }}
/> />
</span> )}
) : ( </div>
<span style={{ color: theme.palette.text.secondary }}> </TableCell>
{Language.upToDateLabel}
</span>
)}
</TableCellLink>
<TableCellLink to={workspacePageLink}> <TableCell>
<LastUsed lastUsedAt={workspace.last_used_at} />
</TableCell>
<TableCell>
<WorkspaceStatusBadge build={workspace.latest_build} /> <WorkspaceStatusBadge build={workspace.latest_build} />
</TableCellLink> </TableCell>
<TableCellLink to={workspacePageLink}>
<TableCell>
<div className={styles.arrowCell}> <div className={styles.arrowCell}>
<KeyboardArrowRight className={styles.arrowRight} /> <KeyboardArrowRight className={styles.arrowRight} />
</div> </div>
</TableCellLink> </TableCell>
</TableRow> </TableRow>
) )
} }
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
clickableTableRow: { row: {
"&:hover td": { cursor: "pointer",
backgroundColor: theme.palette.action.hover,
},
"&:focus": { "&:focus": {
outline: `1px solid ${theme.palette.secondary.dark}`, outline: `1px solid ${theme.palette.secondary.dark}`,
}, outlineOffset: -1,
"& .MuiTableCell-root:last-child": {
paddingRight: theme.spacing(2),
}, },
}, },
arrowRight: { arrowRight: {
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
width: 20, width: 20,
height: 20, height: 20,
}, },
arrowCell: { arrowCell: {
display: "flex", display: "flex",
paddingLeft: theme.spacing(2),
}, },
outdatedLabel: {
color: theme.palette.error.main,
display: "flex",
alignItems: "center",
gap: theme.spacing(0.5),
},
buildTime: {
color: theme.palette.text.secondary,
fontSize: 12,
},
templateIconWrapper: { templateIconWrapper: {
// Same size then the avatar component // Same size then the avatar component
width: 36, width: 36,
@ -142,4 +113,8 @@ const useStyles = makeStyles((theme) => ({
width: "100%", width: "100%",
}, },
}, },
version: {
display: "flex",
},
})) }))

View File

@ -32,10 +32,10 @@ export const WorkspacesTable: FC<
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell width="25%">{Language.name}</TableCell> <TableCell width="30%">{Language.name}</TableCell>
<TableCell width="35%">{Language.template}</TableCell> <TableCell width="25%">{Language.template}</TableCell>
<TableCell width="25%">{Language.version}</TableCell>
<TableCell width="20%">{Language.lastUsed}</TableCell> <TableCell width="20%">{Language.lastUsed}</TableCell>
<TableCell width="20%">{Language.version}</TableCell>
<TableCell width="20%">{Language.status}</TableCell> <TableCell width="20%">{Language.status}</TableCell>
<TableCell width="1%"></TableCell> <TableCell width="1%"></TableCell>
</TableRow> </TableRow>

View File

@ -50,6 +50,7 @@
}, },
"buildData": { "buildData": {
"reason": "Reason", "reason": "Reason",
"duration": "Duration" "duration": "Duration",
"version": "Version"
} }
} }