mirror of
https://github.com/coder/coder.git
synced 2026-10-28 02:42:54 +08:00
feat: Add metadata support to the UI (#3431)
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
import IconButton from "@material-ui/core/IconButton"
|
||||||
|
import { makeStyles } from "@material-ui/core/styles"
|
||||||
|
import Tooltip from "@material-ui/core/Tooltip"
|
||||||
|
import VisibilityOffOutlined from "@material-ui/icons/VisibilityOffOutlined"
|
||||||
|
import VisibilityOutlined from "@material-ui/icons/VisibilityOutlined"
|
||||||
|
import { WorkspaceResource } from "api/typesGenerated"
|
||||||
|
import { FC, useState } from "react"
|
||||||
|
import { TableCellData, TableCellDataPrimary } from "../TableCellData/TableCellData"
|
||||||
|
import { ResourceAvatar } from "./ResourceAvatar"
|
||||||
|
|
||||||
|
const Language = {
|
||||||
|
showLabel: "Show value",
|
||||||
|
hideLabel: "Hide value",
|
||||||
|
}
|
||||||
|
|
||||||
|
const SensitiveValue: React.FC<{ value: string }> = ({ value }) => {
|
||||||
|
const [shouldDisplay, setShouldDisplay] = useState(false)
|
||||||
|
const styles = useStyles()
|
||||||
|
const displayValue = shouldDisplay ? value : "••••••••"
|
||||||
|
const buttonLabel = shouldDisplay ? Language.hideLabel : Language.showLabel
|
||||||
|
const icon = shouldDisplay ? <VisibilityOffOutlined /> : <VisibilityOutlined />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.sensitiveValue}>
|
||||||
|
{displayValue}
|
||||||
|
<Tooltip title={buttonLabel}>
|
||||||
|
<IconButton
|
||||||
|
className={styles.button}
|
||||||
|
onClick={() => {
|
||||||
|
setShouldDisplay((value) => !value)
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
aria-label={buttonLabel}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResourceAvatarDataProps {
|
||||||
|
resource: WorkspaceResource
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ResourceAvatarData: FC<ResourceAvatarDataProps> = ({ resource }) => {
|
||||||
|
const styles = useStyles()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<div className={styles.avatarWrapper}>
|
||||||
|
<ResourceAvatar type={resource.type} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TableCellData>
|
||||||
|
<TableCellDataPrimary highlight>{resource.name}</TableCellDataPrimary>
|
||||||
|
<div className={styles.data}>
|
||||||
|
{resource.metadata?.map((metadata) => (
|
||||||
|
<div key={metadata.key} className={styles.dataRow}>
|
||||||
|
<strong>{metadata.key}:</strong>
|
||||||
|
{metadata.sensitive ? (
|
||||||
|
<SensitiveValue value={metadata.value} />
|
||||||
|
) : (
|
||||||
|
<div>{metadata.value}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</TableCellData>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
|
||||||
|
avatarWrapper: {
|
||||||
|
marginRight: theme.spacing(3),
|
||||||
|
paddingTop: theme.spacing(0.5),
|
||||||
|
},
|
||||||
|
|
||||||
|
data: {
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
fontSize: 14,
|
||||||
|
marginTop: theme.spacing(0.75),
|
||||||
|
display: "grid",
|
||||||
|
gridAutoFlow: "row",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
gap: theme.spacing(0.75),
|
||||||
|
},
|
||||||
|
|
||||||
|
dataRow: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
|
||||||
|
"& strong": {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
sensitiveValue: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
button: {
|
||||||
|
marginLeft: theme.spacing(0.5),
|
||||||
|
color: "inherit",
|
||||||
|
|
||||||
|
"& .MuiSvgIcon-root": {
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
@@ -9,7 +9,6 @@ import useTheme from "@material-ui/styles/useTheme"
|
|||||||
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
|
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
|
||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
|
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
|
||||||
import { AvatarData } from "../../components/AvatarData/AvatarData"
|
|
||||||
import { getDisplayAgentStatus } from "../../util/workspace"
|
import { getDisplayAgentStatus } from "../../util/workspace"
|
||||||
import { AppLink } from "../AppLink/AppLink"
|
import { AppLink } from "../AppLink/AppLink"
|
||||||
import { SSHButton } from "../SSHButton/SSHButton"
|
import { SSHButton } from "../SSHButton/SSHButton"
|
||||||
@@ -18,7 +17,7 @@ import { TableHeaderRow } from "../TableHeaders/TableHeaders"
|
|||||||
import { TerminalLink } from "../TerminalLink/TerminalLink"
|
import { TerminalLink } from "../TerminalLink/TerminalLink"
|
||||||
import { AgentHelpTooltip } from "../Tooltips/AgentHelpTooltip"
|
import { AgentHelpTooltip } from "../Tooltips/AgentHelpTooltip"
|
||||||
import { ResourcesHelpTooltip } from "../Tooltips/ResourcesHelpTooltip"
|
import { ResourcesHelpTooltip } from "../Tooltips/ResourcesHelpTooltip"
|
||||||
import { ResourceAvatar } from "./ResourceAvatar"
|
import { ResourceAvatarData } from "./ResourceAvatarData"
|
||||||
|
|
||||||
const Language = {
|
const Language = {
|
||||||
resources: "Resources",
|
resources: "Resources",
|
||||||
@@ -73,14 +72,7 @@ export const Resources: FC<ResourcesProps> = ({
|
|||||||
/* We need to initialize the agents to display the resource */
|
/* We need to initialize the agents to display the resource */
|
||||||
}
|
}
|
||||||
const agents = resource.agents ?? [null]
|
const agents = resource.agents ?? [null]
|
||||||
const resourceName = (
|
const resourceName = <ResourceAvatarData resource={resource} />
|
||||||
<AvatarData
|
|
||||||
avatar={<ResourceAvatar type={resource.type} />}
|
|
||||||
title={resource.name}
|
|
||||||
subtitle={resource.type}
|
|
||||||
highlightTitle
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return agents.map((agent, agentIndex) => {
|
return agents.map((agent, agentIndex) => {
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -287,12 +287,20 @@ export const MockWorkspaceResource: TypesGen.WorkspaceResource = {
|
|||||||
name: "a-workspace-resource",
|
name: "a-workspace-resource",
|
||||||
type: "google_compute_disk",
|
type: "google_compute_disk",
|
||||||
workspace_transition: "start",
|
workspace_transition: "start",
|
||||||
|
metadata: [
|
||||||
|
{ key: "type", value: "a-workspace-resource", sensitive: false },
|
||||||
|
{ key: "api_key", value: "12345678", sensitive: true },
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MockWorkspaceResource2 = {
|
export const MockWorkspaceResource2 = {
|
||||||
...MockWorkspaceResource,
|
...MockWorkspaceResource,
|
||||||
id: "test-workspace-resource-2",
|
id: "test-workspace-resource-2",
|
||||||
name: "another-workspace-resource",
|
name: "another-workspace-resource",
|
||||||
|
metadata: [
|
||||||
|
{ key: "type", value: "google_compute_disk", sensitive: false },
|
||||||
|
{ key: "size", value: "32GB", sensitive: false },
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MockUserAgent: Types.UserAgent = {
|
export const MockUserAgent: Types.UserAgent = {
|
||||||
|
|||||||
Reference in New Issue
Block a user