mirror of https://github.com/coder/coder.git
191 lines
4.9 KiB
TypeScript
191 lines
4.9 KiB
TypeScript
import {
|
|
css,
|
|
type CSSObject,
|
|
type Interpolation,
|
|
type Theme,
|
|
} from "@emotion/react";
|
|
import AccountIcon from "@mui/icons-material/AccountCircleOutlined";
|
|
import BugIcon from "@mui/icons-material/BugReportOutlined";
|
|
import ChatIcon from "@mui/icons-material/ChatOutlined";
|
|
import LogoutIcon from "@mui/icons-material/ExitToAppOutlined";
|
|
import LaunchIcon from "@mui/icons-material/LaunchOutlined";
|
|
import DocsIcon from "@mui/icons-material/MenuBook";
|
|
import Divider from "@mui/material/Divider";
|
|
import MenuItem from "@mui/material/MenuItem";
|
|
import type { FC } from "react";
|
|
import { Link } from "react-router-dom";
|
|
import type * as TypesGen from "api/typesGenerated";
|
|
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
|
import { usePopover } from "components/Popover/Popover";
|
|
import { Stack } from "components/Stack/Stack";
|
|
|
|
export const Language = {
|
|
accountLabel: "Account",
|
|
signOutLabel: "Sign Out",
|
|
copyrightText: `\u00a9 ${new Date().getFullYear()} Coder Technologies, Inc.`,
|
|
};
|
|
|
|
const styles = {
|
|
info: (theme) => [
|
|
theme.typography.body2 as CSSObject,
|
|
{
|
|
padding: 20,
|
|
},
|
|
],
|
|
userName: {
|
|
fontWeight: 600,
|
|
},
|
|
userEmail: (theme) => ({
|
|
color: theme.palette.text.secondary,
|
|
width: "100%",
|
|
textOverflow: "ellipsis",
|
|
overflow: "hidden",
|
|
}),
|
|
link: {
|
|
textDecoration: "none",
|
|
color: "inherit",
|
|
},
|
|
menuItem: (theme) => css`
|
|
gap: 20px;
|
|
padding: 8px 20px;
|
|
|
|
&:hover {
|
|
background-color: ${theme.palette.action.hover};
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
`,
|
|
menuItemIcon: (theme) => ({
|
|
color: theme.palette.text.secondary,
|
|
width: 20,
|
|
height: 20,
|
|
}),
|
|
menuItemText: {
|
|
fontSize: 14,
|
|
},
|
|
footerText: (theme) => css`
|
|
font-size: 12px;
|
|
text-decoration: none;
|
|
color: ${theme.palette.text.secondary};
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
|
|
& svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
`,
|
|
buildInfo: (theme) => ({
|
|
color: theme.palette.text.primary,
|
|
}),
|
|
} satisfies Record<string, Interpolation<Theme>>;
|
|
|
|
export interface UserDropdownContentProps {
|
|
user: TypesGen.User;
|
|
buildInfo?: TypesGen.BuildInfoResponse;
|
|
supportLinks?: readonly TypesGen.LinkConfig[];
|
|
onSignOut: () => void;
|
|
}
|
|
|
|
export const UserDropdownContent: FC<UserDropdownContentProps> = ({
|
|
buildInfo,
|
|
user,
|
|
supportLinks,
|
|
onSignOut,
|
|
}) => {
|
|
const popover = usePopover();
|
|
|
|
const onPopoverClose = () => {
|
|
popover.setIsOpen(false);
|
|
};
|
|
|
|
const renderMenuIcon = (icon: string): JSX.Element => {
|
|
switch (icon) {
|
|
case "bug":
|
|
return <BugIcon css={styles.menuItemIcon} />;
|
|
case "chat":
|
|
return <ChatIcon css={styles.menuItemIcon} />;
|
|
case "docs":
|
|
return <DocsIcon css={styles.menuItemIcon} />;
|
|
default:
|
|
return (
|
|
<ExternalImage
|
|
src={icon}
|
|
css={{ maxWidth: "20px", maxHeight: "20px" }}
|
|
/>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<Stack css={styles.info} spacing={0}>
|
|
<span css={styles.userName}>{user.username}</span>
|
|
<span css={styles.userEmail}>{user.email}</span>
|
|
</Stack>
|
|
|
|
<Divider css={{ marginBottom: 8 }} />
|
|
|
|
<Link to="/settings/account" css={styles.link}>
|
|
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
|
|
<AccountIcon css={styles.menuItemIcon} />
|
|
<span css={styles.menuItemText}>{Language.accountLabel}</span>
|
|
</MenuItem>
|
|
</Link>
|
|
|
|
<MenuItem css={styles.menuItem} onClick={onSignOut}>
|
|
<LogoutIcon css={styles.menuItemIcon} />
|
|
<span css={styles.menuItemText}>{Language.signOutLabel}</span>
|
|
</MenuItem>
|
|
|
|
{supportLinks && (
|
|
<>
|
|
<Divider />
|
|
{supportLinks.map((link) => (
|
|
<a
|
|
href={includeBuildInfo(link.target, buildInfo)}
|
|
key={link.name}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
css={styles.link}
|
|
>
|
|
<MenuItem css={styles.menuItem} onClick={onPopoverClose}>
|
|
{renderMenuIcon(link.icon)}
|
|
<span css={styles.menuItemText}>{link.name}</span>
|
|
</MenuItem>
|
|
</a>
|
|
))}
|
|
</>
|
|
)}
|
|
|
|
<Divider css={{ marginBottom: "0 !important" }} />
|
|
|
|
<Stack css={styles.info} spacing={0}>
|
|
<a
|
|
title="Browse Source Code"
|
|
css={[styles.footerText, styles.buildInfo]}
|
|
href={buildInfo?.external_url}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
>
|
|
{buildInfo?.version} <LaunchIcon />
|
|
</a>
|
|
|
|
<div css={styles.footerText}>{Language.copyrightText}</div>
|
|
</Stack>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const includeBuildInfo = (
|
|
href: string,
|
|
buildInfo?: TypesGen.BuildInfoResponse,
|
|
): string => {
|
|
return href.replace(
|
|
"{CODER_BUILD_INFO}",
|
|
`${encodeURIComponent(
|
|
`Version: [\`${buildInfo?.version}\`](${buildInfo?.external_url})`,
|
|
)}`,
|
|
);
|
|
};
|