diff --git a/site/e2e/tests/deployment/workspaceProxies.spec.ts b/site/e2e/tests/deployment/workspaceProxies.spec.ts index 0e6edd544cc60..f18ba0a21dde3 100644 --- a/site/e2e/tests/deployment/workspaceProxies.spec.ts +++ b/site/e2e/tests/deployment/workspaceProxies.spec.ts @@ -18,13 +18,12 @@ test("default proxy is online", async ({ page }) => { `table.MuiTable-root tr[data-testid="primary"]`, ); - const workspaceProxyName = workspaceProxyPrimary.locator("td.name span"); - const workspaceProxyURL = workspaceProxyPrimary.locator("td.url"); - const workspaceProxyStatus = workspaceProxyPrimary.locator("td.status span"); + const summary = workspaceProxyPrimary.locator(".summary"); + const status = workspaceProxyPrimary.locator(".status"); - await expect(workspaceProxyName).toHaveText("Default"); - await expect(workspaceProxyURL).toHaveText(`http://localhost:${coderPort}`); - await expect(workspaceProxyStatus).toHaveText("Healthy"); + await expect(summary).toContainText("Default"); + await expect(summary).toContainText(`http://localhost:${coderPort}`); + await expect(status).toContainText("Healthy"); }); test("custom proxy is online", async ({ page }) => { @@ -50,19 +49,16 @@ test("custom proxy is online", async ({ page }) => { waitUntil: "domcontentloaded", }); - const workspaceProxy = page.locator("table.MuiTable-root tr", { + const proxyRow = page.locator("table.MuiTable-root tr", { hasText: proxyName, }); - const workspaceProxyName = workspaceProxy.locator("td.name span"); - const workspaceProxyURL = workspaceProxy.locator("td.url"); - const workspaceProxyStatus = workspaceProxy.locator("td.status span"); + const summary = proxyRow.locator(".summary"); + const status = proxyRow.locator(".status"); - await expect(workspaceProxyName).toHaveText(proxyName); - await expect(workspaceProxyURL).toHaveText( - `http://127.0.0.1:${workspaceProxyPort}`, - ); - await expect(workspaceProxyStatus).toHaveText("Healthy"); + await expect(summary).toContainText(proxyName); + await expect(summary).toContainText(`http://127.0.0.1:${workspaceProxyPort}`); + await expect(status).toContainText("Healthy"); // Tear down the proxy await stopWorkspaceProxy(proxyServer); @@ -82,13 +78,13 @@ const waitUntilWorkspaceProxyIsHealthy = async ( while (retries < maxRetries) { await page.reload(); - const workspaceProxy = page.locator("table.MuiTable-root tr", { + const proxyRow = page.locator("table.MuiTable-root tr", { hasText: proxyName, }); - const workspaceProxyStatus = workspaceProxy.locator("td.status span"); + const status = proxyRow.locator(".status"); try { - await expect(workspaceProxyStatus).toHaveText("Healthy", { + await expect(status).toContainText("Healthy", { timeout: 1_000, }); return; // healthy! diff --git a/site/src/components/Avatar/Avatar.stories.tsx b/site/src/components/Avatar/Avatar.stories.tsx index c1e2f590a91d3..55deeb9073dbe 100644 --- a/site/src/components/Avatar/Avatar.stories.tsx +++ b/site/src/components/Avatar/Avatar.stories.tsx @@ -1,11 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Avatar, AvatarFallback, AvatarImage } from "./Avatar"; +import { Avatar } from "./Avatar"; const meta: Meta = { title: "components/Avatar", component: Avatar, args: { - children: , + src: "https://github.com/kylecarbs.png", }, }; @@ -16,7 +16,7 @@ export const ImageLgSize: Story = { args: { size: "lg" }, }; -export const ImageDefaultSize: Story = {}; +export const ImageMdSize: Story = {}; export const ImageSmSize: Story = { args: { size: "sm" }, @@ -26,18 +26,14 @@ export const IconLgSize: Story = { args: { size: "lg", variant: "icon", - children: ( - - ), + src: "https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png", }, }; -export const IconDefaultSize: Story = { +export const IconMdSize: Story = { args: { variant: "icon", - children: ( - - ), + src: "https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png", }, }; @@ -45,29 +41,36 @@ export const IconSmSize: Story = { args: { variant: "icon", size: "sm", - children: ( - - ), + src: "https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png", + }, +}; + +export const NonSquaredIcon: Story = { + args: { + variant: "icon", + src: "/icon/docker.png", }, }; export const FallbackLgSize: Story = { args: { + src: "", size: "lg", - - children: AR, + fallback: "Adriana Rodrigues", }, }; -export const FallbackDefaultSize: Story = { +export const FallbackMdSize: Story = { args: { - children: AR, + src: "", + fallback: "Adriana Rodrigues", }, }; export const FallbackSmSize: Story = { args: { + src: "", size: "sm", - children: AR, + fallback: "Adriana Rodrigues", }, }; diff --git a/site/src/components/Avatar/Avatar.tsx b/site/src/components/Avatar/Avatar.tsx index 54807455dd030..c09bfaddddf10 100644 --- a/site/src/components/Avatar/Avatar.tsx +++ b/site/src/components/Avatar/Avatar.tsx @@ -1,5 +1,3 @@ -import * as AvatarPrimitive from "@radix-ui/react-avatar"; -import { type VariantProps, cva } from "class-variance-authority"; /** * Copied from shadc/ui on 12/16/2024 * @see {@link https://ui.shadcn.com/docs/components/avatar} @@ -7,8 +5,16 @@ import { type VariantProps, cva } from "class-variance-authority"; * This component was updated to support the variants and match the styles from * the Figma design: * @see {@link https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=711-383&t=xqxOSUk48GvDsjGK-0} + * + * It was also simplified to make usage easier and reduce boilerplate. + * @see {@link https://github.com/coder/coder/pull/15930#issuecomment-2552292440} */ + +import { useTheme } from "@emotion/react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; +import { type VariantProps, cva } from "class-variance-authority"; import * as React from "react"; +import { getExternalImageStylesFromUrl } from "theme/externalImages"; import { cn } from "utils/cn"; const avatarVariants = cva( @@ -16,79 +22,71 @@ const avatarVariants = cva( { variants: { size: { - lg: "h-10 w-10 rounded-[6px] text-sm font-medium", - default: "h-6 w-6 text-2xs", - sm: "h-[18px] w-[18px] text-[8px]", + lg: "h-[--avatar-lg] w-[--avatar-lg] rounded-[6px] text-sm font-medium", + md: "h-[--avatar-default] w-[--avatar-default] text-2xs", + sm: "h-[--avatar-sm] w-[--avatar-sm] text-[8px]", }, variant: { - default: "", - icon: "", + default: null, + icon: null, }, }, defaultVariants: { - size: "default", + size: "md", }, compoundVariants: [ { size: "lg", variant: "icon", - className: "p-[9px]", + className: "p-2", }, { - size: "default", + size: "md", variant: "icon", - className: "p-[3px]", + className: "p-1", }, { size: "sm", variant: "icon", - className: "p-[2px]", + className: "p-[3px]", }, ], }, ); -export interface AvatarProps - extends React.ComponentPropsWithoutRef, - VariantProps {} +export type AvatarProps = AvatarPrimitive.AvatarProps & + VariantProps & { + src?: string; + + fallback?: string; + }; const Avatar = React.forwardRef< React.ElementRef, AvatarProps ->(({ className, size, variant, ...props }, ref) => ( - -)); -Avatar.displayName = AvatarPrimitive.Root.displayName; - -const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AvatarImage.displayName = AvatarPrimitive.Image.displayName; +>(({ className, size, variant, src, fallback, children, ...props }, ref) => { + const theme = useTheme(); -const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + return ( + + + {fallback && ( + + {fallback.charAt(0).toUpperCase()} + + )} + {children} + + ); +}); +Avatar.displayName = AvatarPrimitive.Root.displayName; -export { Avatar, AvatarImage, AvatarFallback }; +export { Avatar }; diff --git a/site/src/components/AvatarCard/AvatarCard.stories.tsx b/site/src/components/Avatar/AvatarCard.stories.tsx similarity index 96% rename from site/src/components/AvatarCard/AvatarCard.stories.tsx rename to site/src/components/Avatar/AvatarCard.stories.tsx index b1202be9d6387..cc8fb56e16c05 100644 --- a/site/src/components/AvatarCard/AvatarCard.stories.tsx +++ b/site/src/components/Avatar/AvatarCard.stories.tsx @@ -13,7 +13,6 @@ export const WithImage: Story = { args: { header: "Coder", imgUrl: "https://avatars.githubusercontent.com/u/95932066?s=200&v=4", - altText: "Coder", subtitle: "56 members", }, }; diff --git a/site/src/components/AvatarCard/AvatarCard.tsx b/site/src/components/Avatar/AvatarCard.tsx similarity index 86% rename from site/src/components/AvatarCard/AvatarCard.tsx rename to site/src/components/Avatar/AvatarCard.tsx index a98dfc54b519d..97df5c6ee765c 100644 --- a/site/src/components/AvatarCard/AvatarCard.tsx +++ b/site/src/components/Avatar/AvatarCard.tsx @@ -1,13 +1,10 @@ import { type CSSObject, useTheme } from "@emotion/react"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; +import { Avatar } from "components/Avatar/Avatar"; import type { FC, ReactNode } from "react"; type AvatarCardProps = { header: string; imgUrl: string; - altText: string; - background?: boolean; - subtitle?: ReactNode; maxWidth?: number | "none"; }; @@ -15,8 +12,6 @@ type AvatarCardProps = { export const AvatarCard: FC = ({ header, imgUrl, - altText, - background, subtitle, maxWidth = "none", }) => { @@ -72,9 +67,7 @@ export const AvatarCard: FC = ({ )} - - {header} - + ); }; diff --git a/site/src/components/AvatarData/AvatarData.stories.tsx b/site/src/components/Avatar/AvatarData.stories.tsx similarity index 100% rename from site/src/components/AvatarData/AvatarData.stories.tsx rename to site/src/components/Avatar/AvatarData.stories.tsx diff --git a/site/src/components/AvatarData/AvatarData.tsx b/site/src/components/Avatar/AvatarData.tsx similarity index 86% rename from site/src/components/AvatarData/AvatarData.tsx rename to site/src/components/Avatar/AvatarData.tsx index 93952bb84d2fc..fab188a234853 100644 --- a/site/src/components/AvatarData/AvatarData.tsx +++ b/site/src/components/Avatar/AvatarData.tsx @@ -1,6 +1,6 @@ import { useTheme } from "@emotion/react"; +import { Avatar } from "components/Avatar/Avatar"; import { Stack } from "components/Stack/Stack"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import type { FC, ReactNode } from "react"; export interface AvatarDataProps { @@ -29,17 +29,17 @@ export const AvatarData: FC = ({ const theme = useTheme(); if (!avatar) { avatar = ( - - {(typeof title === "string" ? title : imgFallbackText) || "-"} - + ); } return ( = ({ build, size }) => { - const theme = useTheme(); - const { status, type } = getDisplayWorkspaceBuildStatus(theme, build); - const badgeType = useClassName( - (css, theme) => css({ backgroundColor: theme.roles[type].fill.solid }), - [type], - ); - - return ( - } - classes={{ badge: cx(classNames.badge, badgeType) }} - > - - - - - ); -}; - -const classNames = { - badge: css({ - borderRadius: "100%", - width: 8, - minWidth: 8, - height: 8, - display: "block", - padding: 0, - }), -}; diff --git a/site/src/components/Filter/SelectFilter.stories.tsx b/site/src/components/Filter/SelectFilter.stories.tsx index ff90bdaa273b3..fcb187c1c098c 100644 --- a/site/src/components/Filter/SelectFilter.stories.tsx +++ b/site/src/components/Filter/SelectFilter.stories.tsx @@ -1,7 +1,7 @@ import { action } from "@storybook/addon-actions"; import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; +import { Avatar } from "components/Avatar/Avatar"; import { useState } from "react"; import { withDesktopViewport } from "testHelpers/storybook"; import { @@ -11,7 +11,7 @@ import { } from "./SelectFilter"; const options: SelectFilterOption[] = Array.from({ length: 50 }, (_, i) => ({ - startIcon: , + startIcon: , label: `Option ${i + 1}`, value: `option-${i + 1}`, })); diff --git a/site/src/components/Filter/UserFilter.tsx b/site/src/components/Filter/UserFilter.tsx index 015bc3d71d439..e1c6d0057d021 100644 --- a/site/src/components/Filter/UserFilter.tsx +++ b/site/src/components/Filter/UserFilter.tsx @@ -1,10 +1,10 @@ import { API } from "api/api"; +import { Avatar } from "components/Avatar/Avatar"; import { SelectFilter, type SelectFilterOption, SelectFilterSearch, } from "components/Filter/SelectFilter"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { useAuthenticated } from "contexts/auth/RequireAuth"; import type { FC } from "react"; import { type UseFilterMenuOptions, useFilterMenu } from "./menu"; @@ -23,11 +23,7 @@ export const useUserFilterMenu = ({ label: me.username, value: me.username, startIcon: ( - + ), }, ...filtered, @@ -45,11 +41,7 @@ export const useUserFilterMenu = ({ label: me.username, value: me.username, startIcon: ( - + ), }; } @@ -61,10 +53,10 @@ export const useUserFilterMenu = ({ label: firstUser.username, value: firstUser.username, startIcon: ( - ), }; @@ -77,11 +69,7 @@ export const useUserFilterMenu = ({ label: user.username, value: user.username, startIcon: ( - + ), })); options = addMeAsFirstOption(options); diff --git a/site/src/components/FullPageLayout/Topbar.tsx b/site/src/components/FullPageLayout/Topbar.tsx index ab51e93b395df..8acff9bc6f4c5 100644 --- a/site/src/components/FullPageLayout/Topbar.tsx +++ b/site/src/components/FullPageLayout/Topbar.tsx @@ -2,10 +2,7 @@ import { css } from "@emotion/css"; import { useTheme } from "@emotion/react"; import Button, { type ButtonProps } from "@mui/material/Button"; import IconButton, { type IconButtonProps } from "@mui/material/IconButton"; -import { - type AvatarProps, - ExternalAvatar, -} from "components/deprecated/Avatar/Avatar"; +import { Avatar, type AvatarProps } from "components/Avatar/Avatar"; import { type FC, type ForwardedRef, @@ -97,14 +94,7 @@ export const TopbarDivider: FC> = (props) => { }; export const TopbarAvatar: FC = (props) => { - return ( - - ); + return ; }; type TopbarIconProps = HTMLAttributes; diff --git a/site/src/components/GroupAvatar/GroupAvatar.stories.tsx b/site/src/components/GroupAvatar/GroupAvatar.stories.tsx deleted file mode 100644 index 7702fcf39ccad..0000000000000 --- a/site/src/components/GroupAvatar/GroupAvatar.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { GroupAvatar } from "./GroupAvatar"; - -const meta: Meta = { - title: "components/GroupAvatar", - component: GroupAvatar, -}; - -export default meta; -type Story = StoryObj; - -const Example: Story = { - args: { - name: "My Group", - avatarURL: "", - }, -}; - -export { Example as GroupAvatar }; diff --git a/site/src/components/GroupAvatar/GroupAvatar.tsx b/site/src/components/GroupAvatar/GroupAvatar.tsx deleted file mode 100644 index fb65c448aef9e..0000000000000 --- a/site/src/components/GroupAvatar/GroupAvatar.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import Group from "@mui/icons-material/Group"; -import Badge from "@mui/material/Badge"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; -import { type ClassName, useClassName } from "hooks/useClassName"; -import type { FC } from "react"; - -export interface GroupAvatarProps { - name: string; - avatarURL?: string; -} - -export const GroupAvatar: FC = ({ name, avatarURL }) => { - const badge = useClassName(classNames.badge, []); - - return ( - } - classes={{ badge }} - > - - {name} - - - ); -}; - -const classNames = { - badge: (css, theme) => - css({ - backgroundColor: theme.palette.background.paper, - border: `1px solid ${theme.palette.divider}`, - borderRadius: "100%", - width: 24, - height: 24, - display: "flex", - alignItems: "center", - justifyContent: "center", - - "& svg": { - width: 14, - height: 14, - }, - }), -} satisfies Record; diff --git a/site/src/components/OrganizationAutocomplete/OrganizationAutocomplete.tsx b/site/src/components/OrganizationAutocomplete/OrganizationAutocomplete.tsx index 6adf28f852f04..348c312ec9fe7 100644 --- a/site/src/components/OrganizationAutocomplete/OrganizationAutocomplete.tsx +++ b/site/src/components/OrganizationAutocomplete/OrganizationAutocomplete.tsx @@ -5,8 +5,8 @@ import TextField from "@mui/material/TextField"; import { checkAuthorization } from "api/queries/authCheck"; import { organizations } from "api/queries/organizations"; import type { AuthorizationCheck, Organization } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { useDebouncedFunction } from "hooks/debounce"; import { type ChangeEvent, @@ -132,9 +132,7 @@ export const OrganizationAutocomplete: FC = ({ ...params.InputProps, onChange: debouncedInputOnChange, startAdornment: value && ( - - {value.name} - + ), endAdornment: ( <> diff --git a/site/src/components/SelectMenu/SelectMenu.stories.tsx b/site/src/components/SelectMenu/SelectMenu.stories.tsx index cae38693899d1..4cbcb465db4bd 100644 --- a/site/src/components/SelectMenu/SelectMenu.stories.tsx +++ b/site/src/components/SelectMenu/SelectMenu.stories.tsx @@ -1,7 +1,7 @@ import { action } from "@storybook/addon-actions"; import type { Meta, StoryObj } from "@storybook/react"; import { userEvent, within } from "@storybook/test"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; +import { Avatar } from "components/Avatar/Avatar"; import { withDesktopViewport } from "testHelpers/storybook"; import { SelectMenu, @@ -25,7 +25,7 @@ const meta: Meta = { } + startIcon={} > {selectedOpt} @@ -36,7 +36,7 @@ const meta: Meta = { {opts.map((o) => ( - + {o} @@ -77,7 +77,7 @@ export const LongButtonText: Story = { } + startIcon={} > {selectedOpt} @@ -88,7 +88,7 @@ export const LongButtonText: Story = { {opts.map((o) => ( - + {o} @@ -115,7 +115,7 @@ export const NoSelectedOption: Story = { {opts.map((o) => ( - + {o} diff --git a/site/src/components/Sidebar/Sidebar.stories.tsx b/site/src/components/Sidebar/Sidebar.stories.tsx index 580d5d59b5be9..6f8d578230b7a 100644 --- a/site/src/components/Sidebar/Sidebar.stories.tsx +++ b/site/src/components/Sidebar/Sidebar.stories.tsx @@ -4,7 +4,7 @@ import SecurityIcon from "@mui/icons-material/LockOutlined"; import AccountIcon from "@mui/icons-material/Person"; import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined"; import type { Meta, StoryObj } from "@storybook/react"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; +import { Avatar } from "components/Avatar/Avatar"; import { Sidebar, SidebarHeader, SidebarNavItem } from "./Sidebar"; const meta: Meta = { @@ -20,7 +20,7 @@ export const Default: Story = { children: ( } + avatar={} title="Jon" subtitle="jon@coder.com" /> diff --git a/site/src/components/Sidebar/Sidebar.tsx b/site/src/components/Sidebar/Sidebar.tsx index b719bb28e83de..987a4b91a3976 100644 --- a/site/src/components/Sidebar/Sidebar.tsx +++ b/site/src/components/Sidebar/Sidebar.tsx @@ -28,7 +28,7 @@ export const SidebarHeader: FC = ({ linkTo, }) => { return ( - + {avatar}
= ({ - template, - ...avatarProps -}) => { - return template.icon ? ( - - ) : ( - {template.display_name || template.name} - ); -}; diff --git a/site/src/components/Timeline/TimelineEntry.tsx b/site/src/components/Timeline/TimelineEntry.tsx index 5c9cedd4cea56..73cdc19295b97 100644 --- a/site/src/components/Timeline/TimelineEntry.tsx +++ b/site/src/components/Timeline/TimelineEntry.tsx @@ -1,5 +1,7 @@ +import type { Interpolation } from "@emotion/react"; import TableRow, { type TableRowProps } from "@mui/material/TableRow"; import { forwardRef } from "react"; +import type { Theme } from "theme"; interface TimelineEntryProps extends TableRowProps { clickable?: boolean; @@ -12,39 +14,44 @@ export const TimelineEntry = forwardRef< return ( [ - { - "&:focus": { - outlineStyle: "solid", - outlineOffset: -1, - outlineWidth: 2, - outlineColor: theme.palette.primary.main, - }, - "& td": { - position: "relative", - overflow: "hidden", - }, - "& td:before": { - position: "absolute", - left: 49, // 50px - (width / 2) - display: "block", - content: "''", - height: "100%", - width: 2, - background: theme.palette.divider, - }, - }, - clickable && { - cursor: "pointer", - - "&:hover": { - backgroundColor: theme.palette.action.hover, - }, - }, - ]} + css={[styles.row, clickable ? styles.clickable : null]} {...props} > {children} ); }); + +const styles = { + row: (theme) => ({ + "--side-padding": "32px", + "&:focus": { + outlineStyle: "solid", + outlineOffset: -1, + outlineWidth: 2, + outlineColor: theme.palette.primary.main, + }, + "& td": { + position: "relative", + overflow: "hidden", + }, + "& td:before": { + "--line-width": "2px", + position: "absolute", + left: "calc((var(--side-padding) + var(--avatar-default)/2) - var(--line-width) / 2)", + display: "block", + content: "''", + height: "100%", + width: "var(--line-width)", + background: theme.palette.divider, + }, + }), + + clickable: (theme) => ({ + cursor: "pointer", + + "&:hover": { + backgroundColor: theme.palette.action.hover, + }, + }), +} satisfies Record>; diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index cf9c9ea6ecc0b..f5bfd109c4a5c 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -6,8 +6,8 @@ import { getErrorMessage } from "api/errors"; import { organizationMembers } from "api/queries/organizations"; import { users } from "api/queries/users"; import type { OrganizationMemberWithUserData, User } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { useDebouncedFunction } from "hooks/debounce"; import { type ChangeEvent, @@ -170,9 +170,11 @@ const InnerAutocomplete = ({ ...params.InputProps, onChange: debouncedInputOnChange, startAdornment: value && ( - - {value.username} - + ), endAdornment: ( <> diff --git a/site/src/components/UserAvatar/UserAvatar.stories.tsx b/site/src/components/UserAvatar/UserAvatar.stories.tsx deleted file mode 100644 index 367dcc2efe7c1..0000000000000 --- a/site/src/components/UserAvatar/UserAvatar.stories.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { UserAvatar } from "./UserAvatar"; - -const meta: Meta = { - title: "components/UserAvatar", - component: UserAvatar, -}; - -export default meta; -type Story = StoryObj; - -export const Jon: Story = { - args: { - username: "sreya", - avatarURL: "https://github.com/sreya.png", - }, -}; - -export const Jonjon: Story = { - args: { - username: "ジョンジョン", - avatarURL: "https://github.com/sreya.png", - }, -}; diff --git a/site/src/components/UserAvatar/UserAvatar.tsx b/site/src/components/UserAvatar/UserAvatar.tsx deleted file mode 100644 index 5244dc6de20e0..0000000000000 --- a/site/src/components/UserAvatar/UserAvatar.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Avatar, type AvatarProps } from "components/deprecated/Avatar/Avatar"; -import type { FC } from "react"; - -export type UserAvatarProps = { - username: string; - avatarURL?: string; -} & AvatarProps; - -export const UserAvatar: FC = ({ - username, - avatarURL, - ...avatarProps -}) => { - return ( - - {username} - - ); -}; diff --git a/site/src/components/deprecated/Avatar/Avatar.stories.tsx b/site/src/components/deprecated/Avatar/Avatar.stories.tsx deleted file mode 100644 index c06390c450d89..0000000000000 --- a/site/src/components/deprecated/Avatar/Avatar.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import PauseIcon from "@mui/icons-material/PauseOutlined"; -import type { Meta, StoryObj } from "@storybook/react"; -import { Avatar, AvatarIcon } from "./Avatar"; - -const meta: Meta = { - title: "components/DeprecatedAvatar", - component: Avatar, -}; - -export default meta; -type Story = StoryObj; - -export const WithLetter: Story = { - args: { - children: "Coder", - }, -}; - -export const WithLetterXL = { - args: { - children: "Coder", - size: "xl", - }, -}; - -export const WithImage = { - args: { - src: "https://avatars.githubusercontent.com/u/95932066?s=200&v=4", - }, -}; - -export const WithImageXL = { - args: { - src: "https://avatars.githubusercontent.com/u/95932066?s=200&v=4", - size: "xl", - }, -}; - -export const WithMuiIcon = { - args: { - background: true, - children: , - }, -}; - -export const WithMuiIconXL = { - args: { - background: true, - children: , - size: "xl", - }, -}; - -export const WithAvatarIcon = { - args: { - background: true, - children: , - }, -}; diff --git a/site/src/components/deprecated/Avatar/Avatar.tsx b/site/src/components/deprecated/Avatar/Avatar.tsx deleted file mode 100644 index 1fcd6208655d2..0000000000000 --- a/site/src/components/deprecated/Avatar/Avatar.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { type Interpolation, type Theme, css, useTheme } from "@emotion/react"; -import MuiAvatar, { - type AvatarProps as MuiAvatarProps, - // biome-ignore lint/nursery/noRestrictedImports: Used as base component -} from "@mui/material/Avatar"; -import { visuallyHidden } from "@mui/utils"; -import { type FC, useId } from "react"; -import { getExternalImageStylesFromUrl } from "theme/externalImages"; - -export type AvatarProps = MuiAvatarProps & { - size?: "xs" | "sm" | "md" | "xl"; - background?: boolean; - fitImage?: boolean; -}; - -const sizeStyles = { - xs: { - width: 16, - height: 16, - fontSize: 8, - fontWeight: 700, - }, - sm: { - width: 24, - height: 24, - fontSize: 12, - fontWeight: 600, - }, - md: {}, - xl: { - width: 48, - height: 48, - fontSize: 24, - }, -} satisfies Record>; - -const fitImageStyles = css` - & .MuiAvatar-img { - object-fit: contain; - } -`; - -/** - * @deprecated Use `Avatar` from `@components/Avatar` instead. - */ -export const Avatar: FC = ({ - size = "md", - fitImage, - children, - background, - ...muiProps -}) => { - const fromName = !muiProps.src && typeof children === "string"; - - return ( - ({ - background: - background || fromName ? theme.palette.divider : undefined, - color: theme.palette.text.primary, - }), - ]} - > - {typeof children === "string" ? firstLetter(children) : children} - - ); -}; - -/** - * @deprecated Use `Avatar` from `@components/Avatar` instead. - */ -export const ExternalAvatar: FC = (props) => { - const theme = useTheme(); - - return ( - - ); -}; - -type AvatarIconProps = { - src: string; - alt: string; -}; - -/** - * Use it to make an img element behaves like a MaterialUI Icon component - * - * @deprecated Use `AvatarIcon` from `@components/Avatar` instead. - */ -export const AvatarIcon: FC = ({ src, alt }) => { - const hookId = useId(); - const avatarId = `${hookId}-avatar`; - - // We use a `visuallyHidden` element instead of setting `alt` to avoid - // splatting the text out on the screen if the image fails to load. - return ( - <> - -
- {alt} -
- - ); -}; - -const firstLetter = (str: string): string => { - if (str.length > 0) { - return str[0].toLocaleUpperCase(); - } - - return ""; -}; diff --git a/site/src/index.css b/site/src/index.css index 1353b200745c0..c97e827b98a0f 100644 --- a/site/src/index.css +++ b/site/src/index.css @@ -31,6 +31,10 @@ --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 240 10% 3.9%; + + --avatar-lg: 2.5rem; + --avatar-default: 1.5rem; + --avatar-sm: 1.125rem; } .dark { --content-primary: 0, 0%, 98%; diff --git a/site/src/components/BuildAvatar/BuildAvatar.stories.tsx b/site/src/modules/builds/BuildAvatar/BuildAvatar.stories.tsx similarity index 91% rename from site/src/components/BuildAvatar/BuildAvatar.stories.tsx rename to site/src/modules/builds/BuildAvatar/BuildAvatar.stories.tsx index 75cedf10ba5c5..ef7f1c49cc7d4 100644 --- a/site/src/components/BuildAvatar/BuildAvatar.stories.tsx +++ b/site/src/modules/builds/BuildAvatar/BuildAvatar.stories.tsx @@ -13,27 +13,21 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const XSSize: Story = { - args: { - size: "xs", - }, -}; - -export const SMSize: Story = { +export const SmSize: Story = { args: { size: "sm", }, }; -export const MDSize: Story = { +export const MdSize: Story = { args: { size: "md", }, }; -export const XLSize: Story = { +export const LgSize: Story = { args: { - size: "xl", + size: "lg", }, }; diff --git a/site/src/modules/builds/BuildAvatar/BuildAvatar.tsx b/site/src/modules/builds/BuildAvatar/BuildAvatar.tsx new file mode 100644 index 0000000000000..e1be56e995b3f --- /dev/null +++ b/site/src/modules/builds/BuildAvatar/BuildAvatar.tsx @@ -0,0 +1,30 @@ +import { useTheme } from "@emotion/react"; +import type { WorkspaceBuild } from "api/typesGenerated"; +import { Avatar, type AvatarProps } from "components/Avatar/Avatar"; +import { BuildIcon } from "components/BuildIcon/BuildIcon"; +import { useClassName } from "hooks/useClassName"; +import type { FC } from "react"; +import { getDisplayWorkspaceBuildStatus } from "utils/workspace"; + +export interface BuildAvatarProps { + build: WorkspaceBuild; + size?: AvatarProps["size"]; +} + +export const BuildAvatar: FC = ({ build, size }) => { + const theme = useTheme(); + const { type } = getDisplayWorkspaceBuildStatus(theme, build); + const iconColor = useClassName( + (css, theme) => css({ color: theme.roles[type].fill.solid }), + [type], + ); + + return ( + + + + ); +}; diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx index caa01ecce0aa6..6fb7428bb0dc1 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx @@ -1,15 +1,15 @@ import { type Interpolation, type Theme, css, useTheme } from "@emotion/react"; import Badge from "@mui/material/Badge"; import type * as TypesGen from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { Popover, PopoverContent, PopoverTrigger, } from "components/deprecated/Popover/Popover"; import { type FC, useState } from "react"; -import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants"; +import { navHeight } from "theme/constants"; import { UserDropdownContent } from "./UserDropdownContent"; export interface UserDropdownProps { @@ -34,10 +34,10 @@ export const UserDropdown: FC = ({ }> - - {template.icon !== "" ? ( - - ) : ( - {template.name} - )} + +
diff --git a/site/src/pages/CreateWorkspacePage/SelectedTemplate.tsx b/site/src/pages/CreateWorkspacePage/SelectedTemplate.tsx index 921dd9d173557..13687e4e45fcb 100644 --- a/site/src/pages/CreateWorkspacePage/SelectedTemplate.tsx +++ b/site/src/pages/CreateWorkspacePage/SelectedTemplate.tsx @@ -1,7 +1,7 @@ import type { Interpolation, Theme } from "@emotion/react"; import type { Template, TemplateExample } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { Stack } from "components/Stack/Stack"; -import { ExternalAvatar } from "components/deprecated/Avatar/Avatar"; import type { FC } from "react"; export interface SelectedTemplateProps { @@ -10,19 +10,13 @@ export interface SelectedTemplateProps { export const SelectedTemplate: FC = ({ template }) => { return ( - - + - {template.name} - + fallback={template.name} + /> diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx index 47d19f48d68eb..8f47c288a840c 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPageView.tsx @@ -10,11 +10,10 @@ import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import type * as TypesGen from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; import { SettingsHeader } from "components/SettingsHeader/SettingsHeader"; import { Stack } from "components/Stack/Stack"; import { TableLoader } from "components/TableLoader/TableLoader"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import { useClickableTableRow } from "hooks/useClickableTableRow"; import type { FC } from "react"; import { Link, useNavigate } from "react-router-dom"; @@ -98,14 +97,10 @@ const OAuth2AppRow: FC = ({ app }) => { return ( - - ) - } - /> + + + {app.name} + diff --git a/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx b/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx index d6e19665cdf92..5ff3b5a626b93 100644 --- a/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx +++ b/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx @@ -8,10 +8,10 @@ import Tooltip from "@mui/material/Tooltip"; import type { ApiErrorResponse } from "api/errors"; import type { ExternalAuth, ExternalAuthDevice } from "api/typesGenerated"; import { Alert, AlertDetail } from "components/Alert/Alert"; +import { Avatar } from "components/Avatar/Avatar"; import { CopyButton } from "components/CopyButton/CopyButton"; import { SignInLayout } from "components/SignInLayout/SignInLayout"; import { Welcome } from "components/Welcome/Welcome"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import type { FC, ReactNode } from "react"; export interface ExternalAuthPageViewProps { @@ -76,7 +76,10 @@ const ExternalAuthPageView: FC = ({

{externalAuth.installations.length > 0 && ( -
+
{externalAuth.installations.map((install) => { if (!install.account) { return; @@ -88,9 +91,10 @@ const ExternalAuthPageView: FC = ({ target="_blank" rel="noreferrer" > - - {install.account.login} - + ); diff --git a/site/src/pages/GroupsPage/GroupPage.tsx b/site/src/pages/GroupsPage/GroupPage.tsx index 1244118aa3840..4af3ce30389fa 100644 --- a/site/src/pages/GroupsPage/GroupPage.tsx +++ b/site/src/pages/GroupsPage/GroupPage.tsx @@ -20,7 +20,8 @@ import { } from "api/queries/groups"; import type { Group, ReducedUser, User } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"; import { EmptyState } from "components/EmptyState/EmptyState"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; @@ -45,7 +46,6 @@ import { TableToolbar, } from "components/TableToolbar/TableToolbar"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { type FC, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQuery, useQueryClient } from "react-query"; @@ -288,12 +288,7 @@ const GroupMemberRow: FC = ({ - } + avatar={} title={member.username} subtitle={member.email} /> diff --git a/site/src/pages/GroupsPage/GroupsPageView.tsx b/site/src/pages/GroupsPage/GroupsPageView.tsx index 8c9f1f8e46601..6188cc09428b9 100644 --- a/site/src/pages/GroupsPage/GroupsPageView.tsx +++ b/site/src/pages/GroupsPage/GroupsPageView.tsx @@ -11,17 +11,16 @@ import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import type { Group } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; -import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; +import { AvatarDataSkeleton } from "components/Avatar/AvatarDataSkeleton"; import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; import { EmptyState } from "components/EmptyState/EmptyState"; -import { GroupAvatar } from "components/GroupAvatar/GroupAvatar"; import { Paywall } from "components/Paywall/Paywall"; import { TableLoaderSkeleton, TableRowSkeleton, } from "components/TableLoader/TableLoader"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import type { FC } from "react"; import { Link as RouterLink, useNavigate } from "react-router-dom"; import { docs } from "utils/docs"; @@ -117,9 +116,9 @@ export const GroupsPageView: FC = ({ } title={group.display_name || group.name} @@ -132,13 +131,13 @@ export const GroupsPageView: FC = ({ {group.members.map((member) => ( - ))} diff --git a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx index 42251f5d5965f..6c226a1dba9ff 100644 --- a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx +++ b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupPage.tsx @@ -24,7 +24,8 @@ import type { ReducedUser, } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"; import { EmptyState } from "components/EmptyState/EmptyState"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; @@ -44,7 +45,6 @@ import { TableToolbar, } from "components/TableToolbar/TableToolbar"; import { MemberAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { type FC, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQuery, useQueryClient } from "react-query"; @@ -302,12 +302,7 @@ const GroupMemberRow: FC = ({ - } + avatar={} title={member.username} subtitle={member.email} /> diff --git a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPageView.tsx b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPageView.tsx index 65e565d75133e..fe109d0ea5718 100644 --- a/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/GroupsPage/GroupsPageView.tsx @@ -11,17 +11,16 @@ import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import type { Group } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; -import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; +import { AvatarDataSkeleton } from "components/Avatar/AvatarDataSkeleton"; import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; import { EmptyState } from "components/EmptyState/EmptyState"; -import { GroupAvatar } from "components/GroupAvatar/GroupAvatar"; import { Paywall } from "components/Paywall/Paywall"; import { TableLoaderSkeleton, TableRowSkeleton, } from "components/TableLoader/TableLoader"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { useClickableTableRow } from "hooks"; import type { FC } from "react"; import { Link as RouterLink, useNavigate } from "react-router-dom"; @@ -124,9 +123,9 @@ const GroupRow: FC = ({ group }) => { } title={group.display_name || group.name} @@ -139,13 +138,13 @@ const GroupRow: FC = ({ group }) => { {group.members.map((member) => ( - ))} diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx index 36002c605e04b..1cc009e1f4de7 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx @@ -16,7 +16,8 @@ import type { User, } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; import { MoreMenu, @@ -28,7 +29,6 @@ import { import { SettingsHeader } from "components/SettingsHeader/SettingsHeader"; import { Stack } from "components/Stack/Stack"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { UserGroupsCell } from "pages/UsersPage/UsersTable/UserGroupsCell"; import { type FC, useState } from "react"; import { TableColumnHelpTooltip } from "./UserTable/TableColumnHelpTooltip"; @@ -107,9 +107,9 @@ export const OrganizationMembersPageView: FC< } title={member.name || member.username} diff --git a/site/src/pages/ManagementSettingsPage/OrganizationSummaryPageView.tsx b/site/src/pages/ManagementSettingsPage/OrganizationSummaryPageView.tsx index 3c37499f34375..c12b3c13a416c 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationSummaryPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationSummaryPageView.tsx @@ -1,11 +1,11 @@ import type { Organization } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { PageHeader, PageHeaderSubtitle, PageHeaderTitle, } from "components/PageHeader/PageHeader"; import { Stack } from "components/Stack/Stack"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import type { FC } from "react"; interface OrganizationSummaryPageViewProps { @@ -23,13 +23,14 @@ export const OrganizationSummaryPageView: FC< paddingTop: 0, }} > - - + +
{organization.display_name || organization.name} diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx index f205194a1aded..097b8fce513e7 100644 --- a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx +++ b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx @@ -26,6 +26,7 @@ import { ActiveUserChart, ActiveUsersTitle, } from "components/ActiveUserChart/ActiveUserChart"; +import { Avatar } from "components/Avatar/Avatar"; import { HelpTooltip, HelpTooltipContent, @@ -35,7 +36,6 @@ import { } from "components/HelpTooltip/HelpTooltip"; import { Loader } from "components/Loader/Loader"; import { Stack } from "components/Stack/Stack"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { addHours, addWeeks, @@ -316,10 +316,7 @@ const UsersLatencyPanel: FC = ({ }} >
- +
{row.username}
= ({ }} >
- +
{row.username}
= ({ } > - - {hasIcon ? ( - - ) : ( - {template.name} - )} + +
diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx index f2cf6bd695fe2..bd8e7e846a011 100644 --- a/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx +++ b/site/src/pages/TemplatePage/TemplateVersionsPage/VersionRow.tsx @@ -2,11 +2,11 @@ import type { CSSObject, Interpolation, Theme } from "@emotion/react"; import Button from "@mui/material/Button"; import TableCell from "@mui/material/TableCell"; import type { TemplateVersion } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { InfoTooltip } from "components/InfoTooltip/InfoTooltip"; import { Pill } from "components/Pill/Pill"; import { Stack } from "components/Stack/Stack"; import { TimelineEntry } from "components/Timeline/TimelineEntry"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { useClickableTableRow } from "hooks/useClickableTableRow"; import type { FC } from "react"; import { useNavigate } from "react-router-dom"; @@ -49,9 +49,9 @@ export const VersionRow: FC = ({ justifyContent="space-between" > - = ({ template }) => { + } title={template.display_name || template.name} linkTo={getLink( diff --git a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPageView.tsx b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPageView.tsx index a69f7657c8bbd..33f1b227e0af2 100644 --- a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPageView.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPageView.tsx @@ -17,10 +17,10 @@ import type { TemplateRole, TemplateUser, } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; import { EmptyState } from "components/EmptyState/EmptyState"; -import { GroupAvatar } from "components/GroupAvatar/GroupAvatar"; import { MoreMenu, MoreMenuContent, @@ -257,9 +257,9 @@ export const TemplatePermissionsPageView: FC< } title={group.display_name || group.name} diff --git a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx index 07c0c58969848..7b0f643eac39f 100644 --- a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx @@ -4,7 +4,7 @@ import CircularProgress from "@mui/material/CircularProgress"; import TextField from "@mui/material/TextField"; import { templaceACLAvailable } from "api/queries/templates"; import type { Group, ReducedUser } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { AvatarData } from "components/Avatar/AvatarData"; import { useDebouncedFunction } from "hooks/debounce"; import { type ChangeEvent, type FC, useState } from "react"; import { useQuery } from "react-query"; diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index bb9bbb7c72732..fe02ff909d169 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -217,7 +217,10 @@ export const TemplateVersionEditor: FC = ({
- + ({ label: org.display_name || org.name, value: org.name, startIcon: ( - + ), }); diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx index 34667c437ceee..d936abb781b90 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx @@ -12,8 +12,9 @@ import TableRow from "@mui/material/TableRow"; import { hasError, isApiValidationError } from "api/errors"; import type { Template, TemplateExample } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { AvatarData } from "components/AvatarData/AvatarData"; -import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; +import { AvatarDataSkeleton } from "components/Avatar/AvatarDataSkeleton"; import { DeprecatedBadge } from "components/Badges/Badges"; import type { useFilter } from "components/Filter/Filter"; import { @@ -36,7 +37,6 @@ import { TableLoaderSkeleton, TableRowSkeleton, } from "components/TableLoader/TableLoader"; -import { ExternalAvatar } from "components/deprecated/Avatar/Avatar"; import { useClickableTableRow } from "hooks/useClickableTableRow"; import { linkToTemplate, useLinks } from "modules/navigation"; import type { FC } from "react"; @@ -110,16 +110,14 @@ const TemplateRow: FC = ({ showOrganizations, template }) => { > 0 - ? template.display_name - : template.name - } + title={template.display_name || template.name} subtitle={template.description} avatar={ - hasIcon && ( - - ) + } /> diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx index 23df9fa7f3e36..49c30523519a1 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountUserGroups.tsx @@ -3,7 +3,7 @@ import Grid from "@mui/material/Grid"; import { isApiError } from "api/errors"; import type { Group } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { AvatarCard } from "components/AvatarCard/AvatarCard"; +import { AvatarCard } from "components/Avatar/AvatarCard"; import { Loader } from "components/Loader/Loader"; import { useDashboard } from "modules/dashboard/useDashboard"; import type { FC } from "react"; @@ -53,9 +53,7 @@ export const AccountUserGroups: FC = ({ {groups.map((group) => ( = ({ ? externalAuth.authenticated : (link?.authenticated ?? false); - let avatar = app.display_icon ? ( - - ) : ( - {name} - ); - - // If the link is authenticated and has a refresh token, show that it will automatically - // attempt to authenticate when the token expires. - if (link?.has_refresh_token && authenticated) { - avatar = ( - - - - } - > - {avatar} - - ); - } - return ( - - {link?.validate_error && ( - <> - + + {name} + {/* + * If the link is authenticated and has a refresh token, show that it will automatically + * attempt to authenticate when the token expires. + */} + {link?.has_refresh_token && authenticated && ( + - Error:{" "} + + + )} + + {link?.validate_error && ( + + + Error:{" "} + + {link?.validate_error} - {link?.validate_error} - - )} + )} +
= ({ app, revoke }) => { return ( - - ) - } - /> + + + {app.name} + diff --git a/site/src/pages/UserSettingsPage/Sidebar.tsx b/site/src/pages/UserSettingsPage/Sidebar.tsx index 196f34d5ce0e1..5cc8c54dcbda9 100644 --- a/site/src/pages/UserSettingsPage/Sidebar.tsx +++ b/site/src/pages/UserSettingsPage/Sidebar.tsx @@ -6,6 +6,7 @@ import NotificationsIcon from "@mui/icons-material/NotificationsNoneOutlined"; import AccountIcon from "@mui/icons-material/Person"; import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined"; import type { User } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge"; import { GitIcon } from "components/Icons/GitIcon"; import { @@ -13,7 +14,6 @@ import { SidebarHeader, SidebarNavItem, } from "components/Sidebar/Sidebar"; -import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { useDashboard } from "modules/dashboard/useDashboard"; import type { FC } from "react"; @@ -29,9 +29,7 @@ export const Sidebar: FC = ({ user }) => { return ( - } + avatar={} title={user.username} subtitle={user.email} /> diff --git a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx index 0005e1fe28a68..7da859a1e0b91 100644 --- a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx +++ b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx @@ -2,14 +2,14 @@ import { useTheme } from "@emotion/react"; import TableCell from "@mui/material/TableCell"; import TableRow from "@mui/material/TableRow"; import type { Region, WorkspaceProxy } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { HealthyBadge, NotHealthyBadge, NotReachableBadge, NotRegisteredBadge, } from "components/Badges/Badges"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import type { ProxyLatencyReport } from "contexts/useProxyLatency"; import type { FC, ReactNode } from "react"; import { getLatencyColor } from "utils/latency"; @@ -40,31 +40,23 @@ export const ProxyRow: FC = ({ proxy, latency }) => { return ( <> - + 0 - ? proxy.display_name - : proxy.name - } + src={proxy.icon_url} + title={proxy.display_name || proxy.name} + subtitle={proxy.path_app_url} avatar={ - proxy.icon_url !== "" && ( - - ) + } /> - - {proxy.path_app_url} - - - {statusBadge} + +
{statusBadge}
= ({ title, messages }) => { css={{ borderBottom: `1px solid ${theme.palette.divider}`, backgroundColor: theme.palette.background.default, - padding: "16px 24px", + padding: "16px 64px", }} >
= ({ - Proxy - URL - Status + Proxy + + Status + Latency diff --git a/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx b/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx index 03d8bebcd2dbf..00f135abb2bdc 100644 --- a/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx +++ b/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx @@ -4,9 +4,9 @@ import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import TableCell from "@mui/material/TableCell"; import type { Group } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { OverflowY } from "components/OverflowY/OverflowY"; import { Stack } from "components/Stack/Stack"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import { Popover, PopoverContent, @@ -99,9 +99,12 @@ export const UserGroupsCell: FC = ({ userGroups }) => { alignItems: "center", }} > - - {groupName} - + = ({ return ( - - + +
Build #{build.build_number} {build.initiator_name} diff --git a/site/src/pages/WorkspacePage/BuildRow.tsx b/site/src/pages/WorkspacePage/BuildRow.tsx index 8b4e9e5a564d1..366ff4b04d8f8 100644 --- a/site/src/pages/WorkspacePage/BuildRow.tsx +++ b/site/src/pages/WorkspacePage/BuildRow.tsx @@ -1,10 +1,10 @@ import type { CSSObject, Interpolation, Theme } from "@emotion/react"; import TableCell from "@mui/material/TableCell"; import type { WorkspaceBuild } from "api/typesGenerated"; -import { BuildAvatar } from "components/BuildAvatar/BuildAvatar"; import { Stack } from "components/Stack/Stack"; import { TimelineEntry } from "components/Timeline/TimelineEntry"; import { useClickable } from "hooks/useClickable"; +import { BuildAvatar } from "modules/builds/BuildAvatar/BuildAvatar"; import type { FC } from "react"; import { useNavigate } from "react-router-dom"; import { diff --git a/site/src/pages/WorkspacePage/ChangeVersionDialog.tsx b/site/src/pages/WorkspacePage/ChangeVersionDialog.tsx index cd12862097f45..453baca597a2c 100644 --- a/site/src/pages/WorkspacePage/ChangeVersionDialog.tsx +++ b/site/src/pages/WorkspacePage/ChangeVersionDialog.tsx @@ -6,14 +6,14 @@ import CircularProgress from "@mui/material/CircularProgress"; import TextField from "@mui/material/TextField"; import type { Template, TemplateVersion } from "api/typesGenerated"; import { Alert } from "components/Alert/Alert"; -import { AvatarData } from "components/AvatarData/AvatarData"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import type { DialogProps } from "components/Dialogs/Dialog"; import { FormFields } from "components/Form/Form"; import { Loader } from "components/Loader/Loader"; import { Pill } from "components/Pill/Pill"; import { Stack } from "components/Stack/Stack"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import { TemplateUpdateMessage } from "modules/templates/TemplateUpdateMessage"; import { type FC, useRef, useState } from "react"; import { createDayString } from "utils/createDayString"; @@ -88,9 +88,10 @@ export const ChangeVersionDialog: FC = ({
  • - {option.name} - + } title={ = ({ - + {ownerName} @@ -319,7 +313,7 @@ const OrganizationBreadcrumb: FC = ({ - + {orgName} @@ -345,12 +339,7 @@ const OrganizationBreadcrumb: FC = ({ subtitle="Organization" avatar={ orgIconUrl && ( - + ) } imgFallbackText={orgName} @@ -381,7 +370,7 @@ const WorkspaceBreadcrumb: FC = ({ - + {workspaceName} @@ -412,11 +401,10 @@ const WorkspaceBreadcrumb: FC = ({ } avatar={ - } imgFallbackText={templateDisplayName} @@ -440,6 +428,7 @@ const styles = { breadcrumbSegment: { display: "flex", + alignItems: "center", flexFlow: "row nowrap", gap: "8px", maxWidth: "160px", diff --git a/site/src/pages/WorkspaceSettingsPage/Sidebar.tsx b/site/src/pages/WorkspaceSettingsPage/Sidebar.tsx index b89e6f5b7d887..604fc2ed70d23 100644 --- a/site/src/pages/WorkspaceSettingsPage/Sidebar.tsx +++ b/site/src/pages/WorkspaceSettingsPage/Sidebar.tsx @@ -2,12 +2,12 @@ import ParameterIcon from "@mui/icons-material/CodeOutlined"; import GeneralIcon from "@mui/icons-material/SettingsOutlined"; import ScheduleIcon from "@mui/icons-material/TimerOutlined"; import type { Workspace } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { Sidebar as BaseSidebar, SidebarHeader, SidebarNavItem, } from "components/Sidebar/Sidebar"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import type { FC } from "react"; interface SidebarProps { @@ -20,7 +20,11 @@ export const Sidebar: FC = ({ username, workspace }) => { + } title={workspace.name} linkTo={`/@${username}/${workspace.name}`} diff --git a/site/src/pages/WorkspacesPage/WorkspacesButton.tsx b/site/src/pages/WorkspacesPage/WorkspacesButton.tsx index d97ff9e2a7551..504a8e156ce5f 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesButton.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesButton.tsx @@ -3,11 +3,11 @@ import OpenIcon from "@mui/icons-material/OpenInNewOutlined"; import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; import type { Template } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { Loader } from "components/Loader/Loader"; import { MenuSearch } from "components/Menu/MenuSearch"; import { OverflowY } from "components/OverflowY/OverflowY"; import { SearchEmpty, searchStyles } from "components/Search/Search"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import { Popover, PopoverContent, @@ -21,8 +21,6 @@ import { type LinkProps as RouterLinkProps, } from "react-router-dom"; -const ICON_SIZE = 18; - type TemplatesQuery = UseQueryResult; interface WorkspacesButtonProps { @@ -141,18 +139,10 @@ const WorkspaceResultsRow: FC = ({ template }) => { }} > - {template.display_name || "-"} - + fallback={template.display_name || template.name} + />
    ({ diff --git a/site/src/pages/WorkspacesPage/WorkspacesEmpty.tsx b/site/src/pages/WorkspacesPage/WorkspacesEmpty.tsx index dcf96ec5c0d8b..80b5602ba2c4c 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesEmpty.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesEmpty.tsx @@ -1,8 +1,8 @@ import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined"; import Button from "@mui/material/Button"; import type { Template } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { TableEmpty } from "components/TableEmpty/TableEmpty"; -import { Avatar } from "components/deprecated/Avatar/Avatar"; import { linkToTemplate, useLinks } from "modules/navigation"; import type { FC } from "react"; import { Link } from "react-router-dom"; @@ -122,14 +122,7 @@ export const WorkspacesEmpty: FC = ({ })} >
    - - {t.name} - +
    diff --git a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx index 2ca1b28ca1632..d3ed0d650e9a6 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx @@ -11,15 +11,15 @@ import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import { visuallyHidden } from "@mui/utils"; import type { Template, Workspace } from "api/typesGenerated"; -import { AvatarData } from "components/AvatarData/AvatarData"; -import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"; +import { Avatar } from "components/Avatar/Avatar"; +import { AvatarData } from "components/Avatar/AvatarData"; +import { AvatarDataSkeleton } from "components/Avatar/AvatarDataSkeleton"; import { InfoTooltip } from "components/InfoTooltip/InfoTooltip"; import { Stack } from "components/Stack/Stack"; import { TableLoaderSkeleton, TableRowSkeleton, } from "components/TableLoader/TableLoader"; -import { ExternalAvatar } from "components/deprecated/Avatar/Avatar"; import { useClickableTableRow } from "hooks/useClickableTableRow"; import { useDashboard } from "modules/dashboard/useDashboard"; import { WorkspaceDormantBadge } from "modules/workspaces/WorkspaceDormantBadge/WorkspaceDormantBadge"; @@ -186,15 +186,11 @@ export const WorkspacesTable: FC = ({
    } avatar={ - - {workspace.name} - + fallback={workspace.name} + /> } />
    diff --git a/site/src/pages/WorkspacesPage/filter/menus.tsx b/site/src/pages/WorkspacesPage/filter/menus.tsx index 7a2d79c606e1f..52e655914cec5 100644 --- a/site/src/pages/WorkspacesPage/filter/menus.tsx +++ b/site/src/pages/WorkspacesPage/filter/menus.tsx @@ -1,5 +1,6 @@ import { API } from "api/api"; -import type { WorkspaceStatus } from "api/typesGenerated"; +import type { Template, WorkspaceStatus } from "api/typesGenerated"; +import { Avatar } from "components/Avatar/Avatar"; import { SelectFilter, type SelectFilterOption, @@ -10,7 +11,6 @@ import { useFilterMenu, } from "components/Filter/menu"; import { StatusIndicator } from "components/StatusIndicator/StatusIndicator"; -import { TemplateAvatar } from "components/TemplateAvatar/TemplateAvatar"; import type { FC } from "react"; import { getDisplayWorkspaceStatus } from "utils/workspace"; @@ -30,7 +30,7 @@ export const useTemplateFilterMenu = ({ return { label: template.display_name || template.name, value: template.name, - startIcon: , + startIcon: , } satisfies SelectFilterOption; } return null; @@ -46,12 +46,23 @@ export const useTemplateFilterMenu = ({ return filteredTemplates.map((template) => ({ label: template.display_name || template.name, value: template.name, - startIcon: , + startIcon: , })); }, }); }; +const TemplateAvatar: FC<{ template: Template }> = ({ template }) => { + return ( + + ); +}; + export type TemplateFilterMenu = ReturnType; type TemplateMenuProps = Readonly<{ pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy