mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Add use template button to template row (#5811)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { KeyboardEvent } from "react"
|
||||
|
||||
interface UseClickableResult {
|
||||
export interface UseClickableResult {
|
||||
tabIndex: 0
|
||||
role: "button"
|
||||
onClick: () => void
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { useClickable, UseClickableResult } from "./useClickable"
|
||||
|
||||
interface UseClickableTableRowResult extends UseClickableResult {
|
||||
className: string
|
||||
hover: true
|
||||
}
|
||||
|
||||
export const useClickableTableRow = (
|
||||
onClick: () => void,
|
||||
): UseClickableTableRowResult => {
|
||||
const styles = useStyles()
|
||||
const clickable = useClickable(onClick)
|
||||
|
||||
return {
|
||||
...clickable,
|
||||
className: styles.row,
|
||||
hover: true,
|
||||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
row: {
|
||||
cursor: "pointer",
|
||||
|
||||
"&:focus": {
|
||||
outline: `1px solid ${theme.palette.secondary.dark}`,
|
||||
outlineOffset: -1,
|
||||
},
|
||||
},
|
||||
}))
|
||||
@@ -1,15 +1,13 @@
|
||||
import Button from "@material-ui/core/Button"
|
||||
import Link from "@material-ui/core/Link"
|
||||
import { makeStyles, Theme } from "@material-ui/core/styles"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import Table from "@material-ui/core/Table"
|
||||
import TableBody from "@material-ui/core/TableBody"
|
||||
import TableCell from "@material-ui/core/TableCell"
|
||||
import TableContainer from "@material-ui/core/TableContainer"
|
||||
import TableHead from "@material-ui/core/TableHead"
|
||||
import TableRow from "@material-ui/core/TableRow"
|
||||
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
|
||||
import AddIcon from "@material-ui/icons/AddOutlined"
|
||||
import useTheme from "@material-ui/styles/useTheme"
|
||||
import { AlertBanner } from "components/AlertBanner/AlertBanner"
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
|
||||
import { Maybe } from "components/Conditionals/Maybe"
|
||||
@@ -28,7 +26,6 @@ import {
|
||||
PageHeaderTitle,
|
||||
} from "../../components/PageHeader/PageHeader"
|
||||
import { Stack } from "../../components/Stack/Stack"
|
||||
import { TableCellLink } from "../../components/TableCellLink/TableCellLink"
|
||||
import { TableLoader } from "../../components/TableLoader/TableLoader"
|
||||
import {
|
||||
HelpTooltip,
|
||||
@@ -39,6 +36,11 @@ import {
|
||||
} from "../../components/Tooltips/HelpTooltip/HelpTooltip"
|
||||
import { EmptyTemplates } from "./EmptyTemplates"
|
||||
import { TemplatesContext } from "xServices/templates/templatesXService"
|
||||
import { useClickableTableRow } from "hooks/useClickableTableRow"
|
||||
import { Template } from "api/typesGenerated"
|
||||
import { combineClasses } from "util/combineClasses"
|
||||
import { colors } from "theme/colors"
|
||||
import ArrowForwardOutlined from "@material-ui/icons/ArrowForwardOutlined"
|
||||
|
||||
export const Language = {
|
||||
developerCount: (activeCount: number): string => {
|
||||
@@ -54,7 +56,6 @@ export const Language = {
|
||||
templateTooltipText:
|
||||
"With templates you can create a common configuration for your workspaces using Terraform.",
|
||||
templateTooltipLink: "Manage templates",
|
||||
createdByLabel: "Created by",
|
||||
}
|
||||
|
||||
const TemplateHelpTooltip: React.FC = () => {
|
||||
@@ -71,6 +72,73 @@ const TemplateHelpTooltip: React.FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const TemplateRow: FC<{ template: Template }> = ({ template }) => {
|
||||
const templatePageLink = `/templates/${template.name}`
|
||||
const hasIcon = template.icon && template.icon !== ""
|
||||
const navigate = useNavigate()
|
||||
const styles = useStyles()
|
||||
const { className: clickableClassName, ...clickableRow } =
|
||||
useClickableTableRow(() => {
|
||||
navigate(templatePageLink)
|
||||
})
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={template.id}
|
||||
data-testid={`template-${template.id}`}
|
||||
{...clickableRow}
|
||||
className={combineClasses([clickableClassName, styles.tableRow])}
|
||||
>
|
||||
<TableCell>
|
||||
<AvatarData
|
||||
title={
|
||||
template.display_name.length > 0
|
||||
? template.display_name
|
||||
: template.name
|
||||
}
|
||||
subtitle={template.description}
|
||||
highlightTitle
|
||||
avatar={
|
||||
hasIcon && (
|
||||
<div className={styles.templateIconWrapper}>
|
||||
<img alt="" src={template.icon} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell className={styles.secondary}>
|
||||
{Language.developerCount(template.active_user_count)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className={styles.secondary}>
|
||||
{formatTemplateBuildTime(template.build_time_stats.start.P50)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell data-chromatic="ignore" className={styles.secondary}>
|
||||
{createDayString(template.updated_at)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className={styles.actionCell}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
className={styles.actionButton}
|
||||
startIcon={<ArrowForwardOutlined />}
|
||||
title={`Create a workspace using the ${template.display_name} template`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
navigate(`/templates/${template.name}/workspace`)
|
||||
}}
|
||||
>
|
||||
Use template
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
export interface TemplatesPageViewProps {
|
||||
context: TemplatesContext
|
||||
}
|
||||
@@ -78,9 +146,6 @@ export interface TemplatesPageViewProps {
|
||||
export const TemplatesPageView: FC<
|
||||
React.PropsWithChildren<TemplatesPageViewProps>
|
||||
> = ({ context }) => {
|
||||
const styles = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const theme: Theme = useTheme()
|
||||
const { templates, error, examples, permissions } = context
|
||||
const isLoading = !templates
|
||||
const isEmpty = Boolean(templates && templates.length === 0)
|
||||
@@ -136,11 +201,10 @@ export const TemplatesPageView: FC<
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell width="34%">{Language.nameLabel}</TableCell>
|
||||
<TableCell width="16%">{Language.usedByLabel}</TableCell>
|
||||
<TableCell width="16%">{Language.buildTimeLabel}</TableCell>
|
||||
<TableCell width="16%">{Language.lastUpdatedLabel}</TableCell>
|
||||
<TableCell width="16%">{Language.createdByLabel}</TableCell>
|
||||
<TableCell width="35%">{Language.nameLabel}</TableCell>
|
||||
<TableCell width="15%">{Language.usedByLabel}</TableCell>
|
||||
<TableCell width="10%">{Language.buildTimeLabel}</TableCell>
|
||||
<TableCell width="15%">{Language.lastUpdatedLabel}</TableCell>
|
||||
<TableCell width="1%"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
@@ -158,91 +222,9 @@ export const TemplatesPageView: FC<
|
||||
</Cond>
|
||||
|
||||
<Cond>
|
||||
{templates?.map((template) => {
|
||||
const templatePageLink = `/templates/${template.name}`
|
||||
const hasIcon = template.icon && template.icon !== ""
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={template.id}
|
||||
hover
|
||||
data-testid={`template-${template.id}`}
|
||||
tabIndex={0}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
navigate(templatePageLink)
|
||||
}
|
||||
}}
|
||||
className={styles.clickableTableRow}
|
||||
>
|
||||
<TableCellLink to={templatePageLink}>
|
||||
<AvatarData
|
||||
title={
|
||||
template.display_name.length > 0
|
||||
? template.display_name
|
||||
: template.name
|
||||
}
|
||||
subtitle={template.description}
|
||||
highlightTitle
|
||||
avatar={
|
||||
hasIcon && (
|
||||
<div className={styles.templateIconWrapper}>
|
||||
<img alt="" src={template.icon} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</TableCellLink>
|
||||
|
||||
<TableCellLink to={templatePageLink}>
|
||||
<span
|
||||
style={{ color: theme.palette.text.secondary }}
|
||||
>
|
||||
{Language.developerCount(
|
||||
template.active_user_count,
|
||||
)}
|
||||
</span>
|
||||
</TableCellLink>
|
||||
|
||||
<TableCellLink to={templatePageLink}>
|
||||
<span
|
||||
style={{ color: theme.palette.text.secondary }}
|
||||
>
|
||||
{formatTemplateBuildTime(
|
||||
template.build_time_stats.start.P50,
|
||||
)}
|
||||
</span>
|
||||
</TableCellLink>
|
||||
|
||||
<TableCellLink
|
||||
data-chromatic="ignore"
|
||||
to={templatePageLink}
|
||||
>
|
||||
<span
|
||||
style={{ color: theme.palette.text.secondary }}
|
||||
>
|
||||
{createDayString(template.updated_at)}
|
||||
</span>
|
||||
</TableCellLink>
|
||||
|
||||
<TableCellLink to={templatePageLink}>
|
||||
<span
|
||||
style={{ color: theme.palette.text.secondary }}
|
||||
>
|
||||
{template.created_by_name}
|
||||
</span>
|
||||
</TableCellLink>
|
||||
|
||||
<TableCellLink to={templatePageLink}>
|
||||
<div className={styles.arrowCell}>
|
||||
<KeyboardArrowRight
|
||||
className={styles.arrowRight}
|
||||
/>
|
||||
</div>
|
||||
</TableCellLink>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
{templates?.map((template) => (
|
||||
<TemplateRow key={template.id} template={template} />
|
||||
))}
|
||||
</Cond>
|
||||
</ChooseOne>
|
||||
</TableBody>
|
||||
@@ -255,27 +237,6 @@ export const TemplatesPageView: FC<
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
clickableTableRow: {
|
||||
"&:hover td": {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
|
||||
"&:focus": {
|
||||
outline: `1px solid ${theme.palette.secondary.dark}`,
|
||||
},
|
||||
|
||||
"& .MuiTableCell-root:last-child": {
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
arrowRight: {
|
||||
color: theme.palette.text.secondary,
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
arrowCell: {
|
||||
display: "flex",
|
||||
},
|
||||
templateIconWrapper: {
|
||||
// Same size then the avatar component
|
||||
width: 36,
|
||||
@@ -286,4 +247,23 @@ const useStyles = makeStyles((theme) => ({
|
||||
width: "100%",
|
||||
},
|
||||
},
|
||||
actionCell: {
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
secondary: {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
tableRow: {
|
||||
"&:hover $actionButton": {
|
||||
color: theme.palette.text.primary,
|
||||
borderColor: colors.gray[11],
|
||||
"&:hover": {
|
||||
borderColor: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
actionButton: {
|
||||
color: theme.palette.text.secondary,
|
||||
transition: "none",
|
||||
},
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user