mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
@ -1,9 +1,8 @@
|
|||||||
import type { Interpolation, Theme } from "@emotion/react";
|
|
||||||
import UserIcon from "@mui/icons-material/PersonOutline";
|
import UserIcon from "@mui/icons-material/PersonOutline";
|
||||||
import Checkbox from "@mui/material/Checkbox";
|
import Checkbox from "@mui/material/Checkbox";
|
||||||
import IconButton from "@mui/material/IconButton";
|
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import type { SlimRole } from "api/typesGenerated";
|
import type { SlimRole } from "api/typesGenerated";
|
||||||
|
import { Button } from "components/Button/Button";
|
||||||
import {
|
import {
|
||||||
HelpTooltip,
|
HelpTooltip,
|
||||||
HelpTooltipContent,
|
HelpTooltipContent,
|
||||||
@ -12,13 +11,11 @@ import {
|
|||||||
HelpTooltipTrigger,
|
HelpTooltipTrigger,
|
||||||
} from "components/HelpTooltip/HelpTooltip";
|
} from "components/HelpTooltip/HelpTooltip";
|
||||||
import { EditSquare } from "components/Icons/EditSquare";
|
import { EditSquare } from "components/Icons/EditSquare";
|
||||||
import { Stack } from "components/Stack/Stack";
|
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "components/deprecated/Popover/Popover";
|
} from "components/deprecated/Popover/Popover";
|
||||||
import { type ClassName, useClassName } from "hooks/useClassName";
|
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
|
|
||||||
const roleDescriptions: Record<string, string> = {
|
const roleDescriptions: Record<string, string> = {
|
||||||
@ -47,23 +44,23 @@ const Option: FC<OptionProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<label htmlFor={name} css={styles.option}>
|
<label htmlFor={name} className="cursor-pointer">
|
||||||
<Stack direction="row" alignItems="flex-start">
|
<div className="flex items-start gap-4">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={name}
|
id={name}
|
||||||
size="small"
|
size="small"
|
||||||
css={styles.checkbox}
|
className="p-0 relative top-px"
|
||||||
value={value}
|
value={value}
|
||||||
checked={isChecked}
|
checked={isChecked}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange(e.currentTarget.value);
|
onChange(e.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack spacing={0}>
|
<div className="flex flex-col">
|
||||||
<strong>{name}</strong>
|
<strong>{name}</strong>
|
||||||
<span css={styles.optionDescription}>{description}</span>
|
<span className="text-xs text-content-secondary">{description}</span>
|
||||||
</Stack>
|
</div>
|
||||||
</Stack>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -85,8 +82,6 @@ export const EditRolesButton: FC<EditRolesButtonProps> = ({
|
|||||||
userLoginType,
|
userLoginType,
|
||||||
oidcRoleSync,
|
oidcRoleSync,
|
||||||
}) => {
|
}) => {
|
||||||
const paper = useClassName(classNames.paper, []);
|
|
||||||
|
|
||||||
const handleChange = (roleName: string) => {
|
const handleChange = (roleName: string) => {
|
||||||
if (selectedRoleNames.has(roleName)) {
|
if (selectedRoleNames.has(roleName)) {
|
||||||
const serialized = [...selectedRoleNames];
|
const serialized = [...selectedRoleNames];
|
||||||
@ -118,23 +113,24 @@ export const EditRolesButton: FC<EditRolesButtonProps> = ({
|
|||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
<Tooltip title="Edit user roles">
|
<Tooltip title="Edit user roles">
|
||||||
<IconButton
|
<Button
|
||||||
|
variant="subtle"
|
||||||
aria-label="Edit user roles"
|
aria-label="Edit user roles"
|
||||||
size="small"
|
size="icon"
|
||||||
css={styles.editButton}
|
className="text-content-secondary hover:text-content-primary"
|
||||||
>
|
>
|
||||||
<EditSquare />
|
<EditSquare />
|
||||||
</IconButton>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|
||||||
<PopoverContent classes={{ paper }} disablePortal={false}>
|
<PopoverContent className="w-80" disablePortal={false}>
|
||||||
<fieldset
|
<fieldset
|
||||||
css={styles.fieldset}
|
className="border-0 m-0 p-0 disabled:opacity-50"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
title="Available roles"
|
title="Available roles"
|
||||||
>
|
>
|
||||||
<Stack css={styles.options} spacing={3}>
|
<div className="flex flex-col gap-4 p-6">
|
||||||
{roles.map((role) => (
|
{roles.map((role) => (
|
||||||
<Option
|
<Option
|
||||||
key={role.name}
|
key={role.name}
|
||||||
@ -145,88 +141,20 @@ export const EditRolesButton: FC<EditRolesButtonProps> = ({
|
|||||||
description={roleDescriptions[role.name] ?? ""}
|
description={roleDescriptions[role.name] ?? ""}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div css={styles.footer}>
|
<div className="p-6 border-t-1 border-solid border-border text-sm">
|
||||||
<Stack direction="row" alignItems="flex-start">
|
<div className="flex gap-4">
|
||||||
<UserIcon css={styles.userIcon} />
|
<UserIcon className="size-icon-sm" />
|
||||||
<Stack spacing={0}>
|
<div className="flex flex-col">
|
||||||
<strong>Member</strong>
|
<strong>Member</strong>
|
||||||
<span css={styles.optionDescription}>
|
<span className="text-xs text-content-secondary">
|
||||||
{roleDescriptions.member}
|
{roleDescriptions.member}
|
||||||
</span>
|
</span>
|
||||||
</Stack>
|
</div>
|
||||||
</Stack>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const classNames = {
|
|
||||||
paper: (css, theme) => css`
|
|
||||||
width: 360px;
|
|
||||||
margin-top: 8px;
|
|
||||||
background: ${theme.palette.background.paper};
|
|
||||||
`,
|
|
||||||
} satisfies Record<string, ClassName>;
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
editButton: (theme) => ({
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
|
|
||||||
"& .MuiSvgIcon-root": {
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
position: "relative",
|
|
||||||
top: -2, // Align the pencil square
|
|
||||||
},
|
|
||||||
|
|
||||||
"&:hover": {
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fieldset: {
|
|
||||||
border: 0,
|
|
||||||
margin: 0,
|
|
||||||
padding: 0,
|
|
||||||
|
|
||||||
"&:disabled": {
|
|
||||||
opacity: 0.5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
padding: 24,
|
|
||||||
},
|
|
||||||
option: {
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
checkbox: {
|
|
||||||
padding: 0,
|
|
||||||
position: "relative",
|
|
||||||
top: 1, // Alignment
|
|
||||||
|
|
||||||
"& svg": {
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
optionDescription: (theme) => ({
|
|
||||||
fontSize: 13,
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
lineHeight: "160%",
|
|
||||||
}),
|
|
||||||
footer: (theme) => ({
|
|
||||||
padding: 24,
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
|
||||||
borderTop: `1px solid ${theme.palette.divider}`,
|
|
||||||
fontSize: 14,
|
|
||||||
}),
|
|
||||||
userIcon: (theme) => ({
|
|
||||||
width: 20, // Same as the checkbox
|
|
||||||
height: 20,
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
}),
|
|
||||||
} satisfies Record<string, Interpolation<Theme>>;
|
|
||||||
|
Reference in New Issue
Block a user