chore: add new avatar component (#15882)

Related to https://github.com/coder/coder/issues/14997

- Add a new `Avatar` component based on the [new avatar
design](https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=711-383&t=xqxOSUk48GvDsjGK-0).
- Deprecate existent `Avatar` component.
This commit is contained in:
Bruno Quaresma
2024-12-17 05:55:31 -03:00
committed by GitHub
parent 468ffd9919
commit badebc79cb
33 changed files with 424 additions and 165 deletions
+40 -26
View File
@@ -1,59 +1,73 @@
import PauseIcon from "@mui/icons-material/PauseOutlined";
import type { Meta, StoryObj } from "@storybook/react";
import { Avatar, AvatarIcon } from "./Avatar";
import { Avatar, AvatarFallback, AvatarImage } from "./Avatar";
const meta: Meta<typeof Avatar> = {
title: "components/Avatar",
component: Avatar,
args: {
children: <AvatarImage src="https://github.com/kylecarbs.png" />,
},
};
export default meta;
type Story = StoryObj<typeof Avatar>;
export const WithLetter: Story = {
export const ImageLgSize: Story = {
args: { size: "lg" },
};
export const ImageDefaultSize: Story = {};
export const ImageSmSize: Story = {
args: { size: "sm" },
};
export const IconLgSize: Story = {
args: {
children: "Coder",
size: "lg",
variant: "icon",
children: (
<AvatarImage src="https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png" />
),
},
};
export const WithLetterXL = {
export const IconDefaultSize: Story = {
args: {
children: "Coder",
size: "xl",
variant: "icon",
children: (
<AvatarImage src="https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png" />
),
},
};
export const WithImage = {
export const IconSmSize: Story = {
args: {
src: "https://avatars.githubusercontent.com/u/95932066?s=200&v=4",
variant: "icon",
size: "sm",
children: (
<AvatarImage src="https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png" />
),
},
};
export const WithImageXL = {
export const FallbackLgSize: Story = {
args: {
src: "https://avatars.githubusercontent.com/u/95932066?s=200&v=4",
size: "xl",
size: "lg",
children: <AvatarFallback>AR</AvatarFallback>,
},
};
export const WithMuiIcon = {
export const FallbackDefaultSize: Story = {
args: {
background: true,
children: <PauseIcon />,
children: <AvatarFallback>AR</AvatarFallback>,
},
};
export const WithMuiIconXL = {
export const FallbackSmSize: Story = {
args: {
background: true,
children: <PauseIcon />,
size: "xl",
},
};
export const WithAvatarIcon = {
args: {
background: true,
children: <AvatarIcon src="/icon/database.svg" alt="Database" />,
size: "sm",
children: <AvatarFallback>AR</AvatarFallback>,
},
};
+89 -111
View File
@@ -1,116 +1,94 @@
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<string, Interpolation<Theme>>;
const fitImageStyles = css`
& .MuiAvatar-img {
object-fit: contain;
}
`;
export const Avatar: FC<AvatarProps> = ({
size = "md",
fitImage,
children,
background,
...muiProps
}) => {
const fromName = !muiProps.src && typeof children === "string";
return (
<MuiAvatar
{...muiProps}
css={[
sizeStyles[size],
fitImage && fitImageStyles,
(theme) => ({
background:
background || fromName ? theme.palette.divider : undefined,
color: theme.palette.text.primary,
}),
]}
>
{typeof children === "string" ? firstLetter(children) : children}
</MuiAvatar>
);
};
export const ExternalAvatar: FC<AvatarProps> = (props) => {
const theme = useTheme();
return (
<Avatar
css={getExternalImageStylesFromUrl(theme.externalImages, props.src)}
{...props}
/>
);
};
type AvatarIconProps = {
src: string;
alt: string;
};
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { type VariantProps, cva } from "class-variance-authority";
/**
* Use it to make an img element behaves like a MaterialUI Icon component
* Copied from shadc/ui on 12/16/2024
* @see {@link https://ui.shadcn.com/docs/components/avatar}
*
* 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}
*/
export const AvatarIcon: FC<AvatarIconProps> = ({ src, alt }) => {
const hookId = useId();
const avatarId = `${hookId}-avatar`;
import * as React from "react";
import { cn } from "utils/cn";
// 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 (
<>
<img
src={src}
alt=""
css={{ maxWidth: "50%" }}
aria-labelledby={avatarId}
/>
<div id={avatarId} css={{ ...visuallyHidden }}>
{alt}
</div>
</>
);
};
const avatarVariants = cva(
"relative flex shrink-0 overflow-hidden rounded border border-solid bg-surface-secondary text-content-secondary",
{
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]",
},
variant: {
default: "",
icon: "",
},
},
defaultVariants: {
size: "default",
},
compoundVariants: [
{
size: "lg",
variant: "icon",
className: "p-[9px]",
},
{
size: "default",
variant: "icon",
className: "p-[3px]",
},
{
size: "sm",
variant: "icon",
className: "p-[2px]",
},
],
},
);
const firstLetter = (str: string): string => {
if (str.length > 0) {
return str[0].toLocaleUpperCase();
}
export interface AvatarProps
extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,
VariantProps<typeof avatarVariants> {}
return "";
};
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
AvatarProps
>(({ className, size, variant, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(avatarVariants({ size, variant, className }))}
{...props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full",
className,
)}
{...props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
export { Avatar, AvatarImage, AvatarFallback };
@@ -1,5 +1,5 @@
import { type CSSObject, useTheme } from "@emotion/react";
import { Avatar } from "components/Avatar/Avatar";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import type { FC, ReactNode } from "react";
type AvatarCardProps = {
@@ -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 {
@@ -2,8 +2,8 @@ import { css, cx } from "@emotion/css";
import { useTheme } from "@emotion/react";
import Badge from "@mui/material/Badge";
import type { WorkspaceBuild } from "api/typesGenerated";
import { Avatar, type AvatarProps } from "components/Avatar/Avatar";
import { BuildIcon } from "components/BuildIcon/BuildIcon";
import { Avatar, type AvatarProps } from "components/deprecated/Avatar/Avatar";
import { useClassName } from "hooks/useClassName";
import type { FC } from "react";
import { getDisplayWorkspaceBuildStatus } from "utils/workspace";
@@ -2,7 +2,10 @@ 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/Avatar/Avatar";
import {
type AvatarProps,
ExternalAvatar,
} from "components/deprecated/Avatar/Avatar";
import {
type FC,
type ForwardedRef,
@@ -1,6 +1,6 @@
import Group from "@mui/icons-material/Group";
import Badge from "@mui/material/Badge";
import { Avatar } from "components/Avatar/Avatar";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { type ClassName, useClassName } from "hooks/useClassName";
import type { FC } from "react";
@@ -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 { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { useDebouncedFunction } from "hooks/debounce";
import {
type ChangeEvent,
@@ -1,5 +1,5 @@
import type { Template } from "api/typesGenerated";
import { Avatar, type AvatarProps } from "components/Avatar/Avatar";
import { Avatar, type AvatarProps } from "components/deprecated/Avatar/Avatar";
import type { FC } from "react";
interface TemplateAvatarProps extends AvatarProps {
@@ -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 { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { useDebouncedFunction } from "hooks/debounce";
import {
type ChangeEvent,
@@ -1,4 +1,4 @@
import { Avatar, type AvatarProps } from "components/Avatar/Avatar";
import { Avatar, type AvatarProps } from "components/deprecated/Avatar/Avatar";
import type { FC } from "react";
export type UserAvatarProps = {
@@ -0,0 +1,59 @@
import PauseIcon from "@mui/icons-material/PauseOutlined";
import type { Meta, StoryObj } from "@storybook/react";
import { Avatar, AvatarIcon } from "./Avatar";
const meta: Meta<typeof Avatar> = {
title: "components/DeprecatedAvatar",
component: Avatar,
};
export default meta;
type Story = StoryObj<typeof Avatar>;
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: <PauseIcon />,
},
};
export const WithMuiIconXL = {
args: {
background: true,
children: <PauseIcon />,
size: "xl",
},
};
export const WithAvatarIcon = {
args: {
background: true,
children: <AvatarIcon src="/icon/database.svg" alt="Database" />,
},
};
@@ -0,0 +1,124 @@
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<string, Interpolation<Theme>>;
const fitImageStyles = css`
& .MuiAvatar-img {
object-fit: contain;
}
`;
/**
* @deprecated Use `Avatar` from `@components/Avatar` instead.
*/
export const Avatar: FC<AvatarProps> = ({
size = "md",
fitImage,
children,
background,
...muiProps
}) => {
const fromName = !muiProps.src && typeof children === "string";
return (
<MuiAvatar
{...muiProps}
css={[
sizeStyles[size],
fitImage && fitImageStyles,
(theme) => ({
background:
background || fromName ? theme.palette.divider : undefined,
color: theme.palette.text.primary,
}),
]}
>
{typeof children === "string" ? firstLetter(children) : children}
</MuiAvatar>
);
};
/**
* @deprecated Use `Avatar` from `@components/Avatar` instead.
*/
export const ExternalAvatar: FC<AvatarProps> = (props) => {
const theme = useTheme();
return (
<Avatar
css={getExternalImageStylesFromUrl(theme.externalImages, props.src)}
{...props}
/>
);
};
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<AvatarIconProps> = ({ 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 (
<>
<img
src={src}
alt=""
css={{ maxWidth: "50%" }}
aria-labelledby={avatarId}
/>
<div id={avatarId} css={{ ...visuallyHidden }}>
{alt}
</div>
</>
);
};
const firstLetter = (str: string): string => {
if (str.length > 0) {
return str[0].toLocaleUpperCase();
}
return "";
};
@@ -1,7 +1,7 @@
import { visuallyHidden } from "@mui/utils";
import type { WorkspaceResource } from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { type FC, useId } from "react";
import { getResourceIconPath } from "utils/workspace";
@@ -5,7 +5,6 @@ import TextField from "@mui/material/TextField";
import type * as TypesGen from "api/typesGenerated";
import { Alert } from "components/Alert/Alert";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Avatar } from "components/Avatar/Avatar";
import {
FormFields,
FormFooter,
@@ -22,6 +21,7 @@ import { Pill } from "components/Pill/Pill";
import { RichParameterInput } from "components/RichParameterInput/RichParameterInput";
import { Stack } from "components/Stack/Stack";
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { type FormikContextType, useFormik } from "formik";
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
import { type FC, useCallback, useEffect, useMemo, useState } from "react";
@@ -1,7 +1,7 @@
import type { Interpolation, Theme } from "@emotion/react";
import type { Template, TemplateExample } from "api/typesGenerated";
import { ExternalAvatar } 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,11 +10,11 @@ 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 { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
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";
@@ -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 {
@@ -11,7 +11,6 @@ import type {
Template,
TemplateVersion,
} from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog";
import { Margins } from "components/Margins/Margins";
@@ -30,6 +29,7 @@ import {
} from "components/PageHeader/PageHeader";
import { Pill } from "components/Pill/Pill";
import { Stack } from "components/Stack/Stack";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { linkToTemplate, useLinks } from "modules/navigation";
import type { FC } from "react";
import { useQuery } from "react-query";
@@ -3,12 +3,12 @@ import SecurityIcon from "@mui/icons-material/LockOutlined";
import GeneralIcon from "@mui/icons-material/SettingsOutlined";
import ScheduleIcon from "@mui/icons-material/TimerOutlined";
import type { Template } from "api/typesGenerated";
import { ExternalAvatar } from "components/Avatar/Avatar";
import {
Sidebar as BaseSidebar,
SidebarHeader,
SidebarNavItem,
} from "components/Sidebar/Sidebar";
import { ExternalAvatar } from "components/deprecated/Avatar/Avatar";
import { linkToTemplate, useLinks } from "modules/navigation";
import type { FC } from "react";
@@ -12,7 +12,6 @@ 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 { ExternalAvatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
import { DeprecatedBadge } from "components/Badges/Badges";
@@ -37,6 +36,7 @@ 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";
@@ -20,7 +20,6 @@ import type {
ListUserExternalAuthResponse,
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Avatar, ExternalAvatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { Loader } from "components/Loader/Loader";
import {
@@ -31,6 +30,7 @@ import {
ThreeDotsButton,
} from "components/MoreMenu/MoreMenu";
import { TableEmpty } from "components/TableEmpty/TableEmpty";
import { Avatar, ExternalAvatar } from "components/deprecated/Avatar/Avatar";
import type { ExternalAuthPollingState } from "pages/CreateWorkspacePage/CreateWorkspacePage";
import { type FC, useCallback, useEffect, useState } from "react";
import { useQuery } from "react-query";
@@ -7,9 +7,9 @@ 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 { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { TableLoader } from "components/TableLoader/TableLoader";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import type { FC } from "react";
export type OAuth2ProviderPageViewProps = {
@@ -2,7 +2,6 @@ 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 { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import {
HealthyBadge,
@@ -10,6 +9,7 @@ import {
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";
@@ -4,7 +4,6 @@ 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 {
Popover,
@@ -12,6 +11,7 @@ import {
PopoverTrigger,
} from "components/Popover/Popover";
import { Stack } from "components/Stack/Stack";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import type { FC } from "react";
type GroupsCellProps = {
@@ -6,7 +6,6 @@ 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 { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import type { DialogProps } from "components/Dialogs/Dialog";
@@ -14,6 +13,7 @@ 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";
@@ -6,7 +6,6 @@ import Link from "@mui/material/Link";
import Tooltip from "@mui/material/Tooltip";
import { workspaceQuota } from "api/queries/workspaceQuota";
import type * as TypesGen from "api/typesGenerated";
import { ExternalAvatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import {
Topbar,
@@ -19,6 +18,7 @@ import {
import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip";
import { Popover, PopoverTrigger } from "components/Popover/Popover";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import { ExternalAvatar } from "components/deprecated/Avatar/Avatar";
import { useDashboard } from "modules/dashboard/useDashboard";
import { linkToTemplate, useLinks } from "modules/navigation";
import { WorkspaceStatusBadge } from "modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge";
@@ -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 {
@@ -3,7 +3,6 @@ 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";
@@ -13,6 +12,7 @@ import {
PopoverTrigger,
} from "components/Popover/Popover";
import { SearchEmpty, searchStyles } from "components/Search/Search";
import { Avatar } from "components/deprecated/Avatar/Avatar";
import { linkToTemplate, useLinks } from "modules/navigation";
import { type FC, type ReactNode, useState } from "react";
import type { UseQueryResult } from "react-query";
@@ -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";
@@ -11,7 +11,6 @@ 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 { ExternalAvatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/AvatarData/AvatarData";
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
@@ -20,6 +19,7 @@ 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";