Skip to content

Commit e874d53

Browse files
feat: Add resource icons (coder#3118)
1 parent 7d07e67 commit e874d53

File tree

6 files changed

+120
-11
lines changed

6 files changed

+120
-11
lines changed

site/src/components/AvatarData/AvatarData.tsx

+14-4
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,25 @@ export interface AvatarDataProps {
1515
subtitle: string
1616
highlightTitle?: boolean
1717
link?: string
18+
avatar?: React.ReactNode
1819
}
1920

20-
export const AvatarData: FC<AvatarDataProps> = ({ title, subtitle, link, highlightTitle }) => {
21+
export const AvatarData: FC<AvatarDataProps> = ({
22+
title,
23+
subtitle,
24+
link,
25+
highlightTitle,
26+
avatar,
27+
}) => {
2128
const styles = useStyles()
2229

30+
if (!avatar) {
31+
avatar = <Avatar>{firstLetter(title)}</Avatar>
32+
}
33+
2334
return (
2435
<div className={styles.root}>
25-
<Avatar className={styles.avatar}>{firstLetter(title)}</Avatar>
36+
<div className={styles.avatarWrapper}>{avatar}</div>
2637

2738
{link ? (
2839
<Link to={link} underline="none" component={RouterLink}>
@@ -46,8 +57,7 @@ const useStyles = makeStyles((theme) => ({
4657
display: "flex",
4758
alignItems: "center",
4859
},
49-
avatar: {
60+
avatarWrapper: {
5061
marginRight: theme.spacing(1.5),
51-
background: "hsl(219, 8%, 52%)",
5262
},
5363
}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Story } from "@storybook/react"
2+
import { ResourceAvatar, ResourceAvatarProps } from "./ResourceAvatar"
3+
4+
export default {
5+
title: "components/ResourceAvatar",
6+
component: ResourceAvatar,
7+
}
8+
9+
const Template: Story<ResourceAvatarProps> = (args) => <ResourceAvatar {...args} />
10+
11+
export const VolumeResource = Template.bind({})
12+
VolumeResource.args = {
13+
type: "docker_volume",
14+
}
15+
16+
export const ComputeResource = Template.bind({})
17+
ComputeResource.args = {
18+
type: "docker_container",
19+
}
20+
21+
export const ImageResource = Template.bind({})
22+
ImageResource.args = {
23+
type: "docker_image",
24+
}
25+
26+
export const NullResource = Template.bind({})
27+
NullResource.args = {
28+
type: "null_resource",
29+
}
30+
31+
export const UnkownResource = Template.bind({})
32+
UnkownResource.args = {
33+
type: "noexistentvalue",
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Avatar from "@material-ui/core/Avatar"
2+
import { makeStyles } from "@material-ui/core/styles"
3+
import FolderIcon from "@material-ui/icons/FolderOutlined"
4+
import HelpIcon from "@material-ui/icons/HelpOutlined"
5+
import ImageIcon from "@material-ui/icons/ImageOutlined"
6+
import MemoryIcon from "@material-ui/icons/MemoryOutlined"
7+
import React from "react"
8+
9+
// For this special case, we need to apply a different style because how this
10+
// particular icon has been designed
11+
const AdjustedMemoryIcon: typeof MemoryIcon = ({ style, ...props }) => {
12+
return <MemoryIcon style={{ ...style, fontSize: 24 }} {...props} />
13+
}
14+
15+
const iconByResource: Record<string, typeof MemoryIcon> = {
16+
docker_volume: FolderIcon,
17+
docker_container: AdjustedMemoryIcon,
18+
docker_image: ImageIcon,
19+
kubernetes_persistent_volume_claim: FolderIcon,
20+
kubernetes_pod: AdjustedMemoryIcon,
21+
google_compute_disk: FolderIcon,
22+
google_compute_instance: AdjustedMemoryIcon,
23+
aws_instance: AdjustedMemoryIcon,
24+
kubernetes_deployment: AdjustedMemoryIcon,
25+
null_resource: HelpIcon,
26+
}
27+
28+
export type ResourceAvatarProps = { type: string }
29+
30+
export const ResourceAvatar: React.FC<ResourceAvatarProps> = ({ type }) => {
31+
// this resource can return undefined
32+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
33+
const IconComponent = iconByResource[type] ?? HelpIcon
34+
const styles = useStyles()
35+
36+
return (
37+
<Avatar className={styles.resourceAvatar}>
38+
<IconComponent style={{ fontSize: 20 }} />
39+
</Avatar>
40+
)
41+
}
42+
43+
const useStyles = makeStyles((theme) => ({
44+
resourceAvatar: {
45+
color: theme.palette.info.contrastText,
46+
backgroundColor: theme.palette.info.main,
47+
},
48+
}))

site/src/components/Resources/Resources.tsx

+18-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import TableRow from "@material-ui/core/TableRow"
77
import useTheme from "@material-ui/styles/useTheme"
88
import { FC } from "react"
99
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
10+
import { AvatarData } from "../../components/AvatarData/AvatarData"
1011
import { getDisplayAgentStatus } from "../../util/workspace"
1112
import { AppLink } from "../AppLink/AppLink"
1213
import { SSHButton } from "../SSHButton/SSHButton"
@@ -15,6 +16,7 @@ import { TableHeaderRow } from "../TableHeaders/TableHeaders"
1516
import { TerminalLink } from "../TerminalLink/TerminalLink"
1617
import { AgentHelpTooltip } from "../Tooltips/AgentHelpTooltip"
1718
import { ResourcesHelpTooltip } from "../Tooltips/ResourcesHelpTooltip"
19+
import { ResourceAvatar } from "./ResourceAvatar"
1820

1921
const Language = {
2022
resources: "Resources",
@@ -68,17 +70,23 @@ export const Resources: FC<ResourcesProps> = ({
6870
/* We need to initialize the agents to display the resource */
6971
}
7072
const agents = resource.agents ?? [null]
73+
const resourceName = (
74+
<AvatarData
75+
avatar={<ResourceAvatar type={resource.type} />}
76+
title={resource.name}
77+
subtitle={resource.type}
78+
highlightTitle
79+
/>
80+
)
81+
7182
return agents.map((agent, agentIndex) => {
7283
{
7384
/* If there is no agent, just display the resource name */
7485
}
7586
if (!agent) {
7687
return (
7788
<TableRow key={`${resource.id}-${agentIndex}`}>
78-
<TableCell>
79-
{resource.name}
80-
<span className={styles.resourceType}>{resource.type}</span>
81-
</TableCell>
89+
<TableCell>{resourceName}</TableCell>
8290
<TableCell colSpan={3}></TableCell>
8391
</TableRow>
8492
)
@@ -91,8 +99,7 @@ export const Resources: FC<ResourcesProps> = ({
9199
{/* The rowspan should be the same than the number of agents */}
92100
{agentIndex === 0 && (
93101
<TableCell className={styles.resourceNameCell} rowSpan={agents.length}>
94-
{resource.name}
95-
<span className={styles.resourceType}>{resource.type}</span>
102+
{resourceName}
96103
</TableCell>
97104
)}
98105

@@ -149,6 +156,11 @@ const useStyles = makeStyles((theme) => ({
149156
border: 0,
150157
},
151158

159+
resourceAvatar: {
160+
color: "#FFF",
161+
backgroundColor: "#3B73D8",
162+
},
163+
152164
resourceNameCell: {
153165
borderRight: `1px solid ${theme.palette.divider}`,
154166
},

site/src/theme/overrides.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ export const getOverrides = (palette: PaletteOptions) => {
1717
},
1818
MuiAvatar: {
1919
root: {
20-
borderColor: palette.divider,
2120
width: 36,
2221
height: 36,
2322
fontSize: 18,
2423
},
24+
colorDefault: {
25+
backgroundColor: "#a1adc9",
26+
},
2527
},
2628
MuiButton: {
2729
root: {

site/src/theme/palettes.ts

+3
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@ export const darkPalette: PaletteOptions = {
2828
success: {
2929
main: "hsl(142, 58%, 41%)",
3030
},
31+
info: {
32+
main: "hsl(219, 67%, 54%)",
33+
},
3134
}

0 commit comments

Comments
 (0)