From 8cbf9aab4278bccdc4490ee0e824a6dd2625e97e Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 20:01:29 +0000 Subject: [PATCH 01/11] Update EnterpriseSnackbar.tsx --- .../GlobalSnackbar/EnterpriseSnackbar.tsx | 51 +++++-------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx b/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx index e7c9fbcce3863..725f6ef907c06 100644 --- a/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx +++ b/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx @@ -1,11 +1,10 @@ -import type { Interpolation, Theme } from "@emotion/react"; import IconButton from "@mui/material/IconButton"; import Snackbar, { type SnackbarProps as MuiSnackbarProps, } from "@mui/material/Snackbar"; -import { type ClassName, useClassName } from "hooks/useClassName"; import { X as XIcon } from "lucide-react"; import type { FC } from "react"; +import { cn } from "utils/cn"; type EnterpriseSnackbarVariant = "error" | "info" | "success"; @@ -35,7 +34,6 @@ export const EnterpriseSnackbar: FC = ({ action, ...snackbarProps }) => { - const content = useClassName(classNames.content(variant), [variant]); return ( = ({ horizontal: "right", }} action={ -
+
{action} - +
} ContentProps={{ ...ContentProps, - className: content, + className: cn( + "rounded-lg bg-surface-secondary text-content-primary shadow", + "py-2 pl-6 pr-4 items-[inherit] border-0 border-l-[4px]", + variantColor(variant) + ), }} onClose={onClose} {...snackbarProps} @@ -67,39 +68,13 @@ export const EnterpriseSnackbar: FC = ({ ); }; -const variantColor = (variant: EnterpriseSnackbarVariant, theme: Theme) => { +const variantColor = (variant: EnterpriseSnackbarVariant) => { switch (variant) { case "error": - return theme.palette.error.main; + return "border-highlight-red"; case "info": - return theme.palette.info.main; + return "border-highlight-sky"; case "success": - return theme.palette.success.main; + return "border-border-success"; } }; - -const classNames = { - content: - (variant: EnterpriseSnackbarVariant): ClassName => - (css, theme) => - css` - border: 1px solid ${theme.palette.divider}; - border-left: 4px solid ${variantColor(variant, theme)}; - border-radius: 8px; - padding: 8px 24px 8px 16px; - box-shadow: ${theme.shadows[6]}; - align-items: inherit; - background-color: ${theme.palette.background.paper}; - color: ${theme.palette.text.secondary}; - `, -}; - -const styles = { - actionWrapper: { - display: "flex", - alignItems: "center", - }, - closeIcon: (theme) => ({ - color: theme.palette.primary.contrastText, - }), -} satisfies Record>; From f1ed76e7e8b697126be30117906056b5eae35f23 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 21:48:30 +0000 Subject: [PATCH 02/11] Update Sidebar.tsx --- site/src/@types/storybook.d.ts | 2 + .../GlobalSnackbar/EnterpriseSnackbar.tsx | 3 +- .../components/Sidebar/Sidebar.stories.tsx | 94 ++++++++++++++----- site/src/components/Sidebar/Sidebar.tsx | 87 ++++------------- 4 files changed, 91 insertions(+), 95 deletions(-) diff --git a/site/src/@types/storybook.d.ts b/site/src/@types/storybook.d.ts index ccdecd690c9c8..599324a291ae4 100644 --- a/site/src/@types/storybook.d.ts +++ b/site/src/@types/storybook.d.ts @@ -8,6 +8,7 @@ import type { } from "api/typesGenerated"; import type { Permissions } from "modules/permissions"; import type { QueryKey } from "react-query"; +import type { ReactRouterAddonStoryParameters } from "storybook-addon-remix-react-router"; declare module "@storybook/react-vite" { type WebSocketEvent = @@ -24,5 +25,6 @@ declare module "@storybook/react-vite" { permissions?: Partial; deploymentValues?: DeploymentValues; deploymentOptions?: SerpentOption[]; + reactRouter?: ReactRouterAddonStoryParameters; } } diff --git a/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx b/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx index 725f6ef907c06..17036fb2be8e4 100644 --- a/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx +++ b/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx @@ -34,7 +34,6 @@ export const EnterpriseSnackbar: FC = ({ action, ...snackbarProps }) => { - return ( = ({ className: cn( "rounded-lg bg-surface-secondary text-content-primary shadow", "py-2 pl-6 pr-4 items-[inherit] border-0 border-l-[4px]", - variantColor(variant) + variantColor(variant), ), }} onClose={onClose} diff --git a/site/src/components/Sidebar/Sidebar.stories.tsx b/site/src/components/Sidebar/Sidebar.stories.tsx index 083bffa423fe4..f352118f5f69e 100644 --- a/site/src/components/Sidebar/Sidebar.stories.tsx +++ b/site/src/components/Sidebar/Sidebar.stories.tsx @@ -7,6 +7,7 @@ import { LockIcon, UserIcon, } from "lucide-react"; +import { Outlet } from "react-router"; import { Sidebar, SidebarHeader, SidebarNavItem } from "./Sidebar"; const meta: Meta = { @@ -18,30 +19,73 @@ export default meta; type Story = StoryObj; export const Default: Story = { - args: { - children: ( - - } - title="Jon" - subtitle="jon@coder.com" - /> - - Account - - - Schedule - - - Security - - - SSH Keys - - - Tokens - - - ), + decorators: [ + (Story) => { + return ( +
+ + +
+ ); + }, + ], + render: () => ( + + } + title="Jon" + subtitle="jon@coder.com" + /> + + Account + + + Schedule + + + Security + + + SSH Keys + + + Tokens + + + ), + parameters: { + reactRouter: { + location: { + path: "/account", + }, + routing: [ + { + path: "/", + useStoryElement: true, + children: [ + { + path: "account", + element: <>Account page, + }, + { + path: "schedule", + element: <>Schedule page, + }, + { + path: "security", + element: <>Security page, + }, + { + path: "ssh-keys", + element: <>SSH Keys, + }, + { + path: "tokens", + element: <>Tokens page, + }, + ], + }, + ], + }, }, }; diff --git a/site/src/components/Sidebar/Sidebar.tsx b/site/src/components/Sidebar/Sidebar.tsx index 813835baeb277..249362788d2c0 100644 --- a/site/src/components/Sidebar/Sidebar.tsx +++ b/site/src/components/Sidebar/Sidebar.tsx @@ -1,7 +1,4 @@ -import { cx } from "@emotion/css"; -import type { CSSObject, Interpolation, Theme } from "@emotion/react"; import { Stack } from "components/Stack/Stack"; -import { type ClassName, useClassName } from "hooks/useClassName"; import type { ElementType, FC, ReactNode } from "react"; import { Link, NavLink } from "react-router"; import { cn } from "utils/cn"; @@ -21,6 +18,11 @@ interface SidebarHeaderProps { linkTo?: string; } +const titleStyles = { + normal: + "text-semibold overflow-hidden whitespace-nowrap text-content-primary", +}; + export const SidebarHeader: FC = ({ avatar, title, @@ -28,7 +30,7 @@ export const SidebarHeader: FC = ({ linkTo, }) => { return ( - + {avatar}
= ({ }} > {linkTo ? ( - + {title} ) : ( - {title} + {title} )} - {subtitle} + + {subtitle} +
); @@ -88,14 +92,18 @@ export const SidebarNavItem: FC = ({ href, icon: Icon, }) => { - const link = useClassName(classNames.link, []); - const activeLink = useClassName(classNames.activeLink, []); - return ( cx([link, isActive && activeLink])} + className={({ isActive }) => + cn( + "block relative text-sm text-inherit mb-px p-3 pl-4 rounded-sm", + "transition-colors no-underline hover:bg-surface-secondary", + isActive && + "bg-surface-secondary border-0 border-solid border-l-[3px] border-highlight-sky", + ) + } > @@ -104,60 +112,3 @@ export const SidebarNavItem: FC = ({ ); }; - -const styles = { - info: (theme) => ({ - ...(theme.typography.body2 as CSSObject), - marginBottom: 16, - }), - - title: (theme) => ({ - fontWeight: 600, - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - color: theme.palette.text.primary, - textDecoration: "none", - }), - subtitle: (theme) => ({ - color: theme.palette.text.secondary, - fontSize: 12, - overflow: "hidden", - textOverflow: "ellipsis", - }), -} satisfies Record>; - -const classNames = { - link: (css, theme) => css` - color: inherit; - display: block; - font-size: 14px; - text-decoration: none; - padding: 12px 12px 12px 16px; - border-radius: 4px; - transition: background-color 0.15s ease-in-out; - margin-bottom: 1px; - position: relative; - - &:hover { - background-color: ${theme.palette.action.hover}; - } - `, - - activeLink: (css, theme) => css` - background-color: ${theme.palette.action.hover}; - - &:before { - content: ""; - display: block; - width: 3px; - height: 100%; - position: absolute; - left: 0; - top: 0; - background-color: ${theme.palette.primary.main}; - border-top-left-radius: 8px; - border-bottom-left-radius: 8px; - } - `, -} satisfies Record; From c412562d81d163b51fd5f6a2e8ceca4a995d2bb4 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 22:27:28 +0000 Subject: [PATCH 03/11] Update HealthLayout.tsx --- site/src/pages/HealthPage/HealthLayout.tsx | 104 +++++---------------- 1 file changed, 25 insertions(+), 79 deletions(-) diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index e1bdec0973b83..d7f5e2bfc9115 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -1,4 +1,3 @@ -import { cx } from "@emotion/css"; import { useTheme } from "@emotion/react"; import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined"; import ReplayIcon from "@mui/icons-material/Replay"; @@ -9,13 +8,13 @@ import { health, refreshHealth } from "api/queries/debug"; import type { HealthSeverity } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; -import { type ClassName, useClassName } from "hooks/useClassName"; import kebabCase from "lodash/fp/kebabCase"; import { DashboardFullPage } from "modules/dashboard/DashboardLayout"; import { type FC, Suspense } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { NavLink, Outlet } from "react-router"; +import { cn } from "utils/cn"; import { createDayString } from "utils/createDayString"; import { pageTitle } from "utils/page"; import { HealthIcon } from "./Content"; @@ -44,8 +43,12 @@ export const HealthLayout: FC = () => { } as const; const visibleSections = filterVisibleSections(sections); - const link = useClassName(classNames.link, []); - const activeLink = useClassName(classNames.activeLink, []); + const link = ` + text-content-secondary border-none text-sm w-full flex items-center gap-3 + text-left h-9 px-6 cursor-pointer no-underline transition-colors + hover:bg-surface-secondary hover:text-content-primary + `; + const activeLink = "bg-surface-secondary text-content-primary"; if (isLoading) { return ( @@ -71,36 +74,17 @@ export const HealthLayout: FC = () => {
@@ -116,12 +100,12 @@ export const HealthLayout: FC = () => { {isRefreshing ? ( ) : ( - + )}
-
+
{healthStatus.healthy ? "Healthy" : "Unhealthy"}
{ > {healthStatus.healthy ? Object.keys(visibleSections).some((key) => { - const section = - healthStatus[key as keyof typeof visibleSections]; - return section.warnings && section.warnings.length > 0; - }) + const section = + healthStatus[key as keyof typeof visibleSections]; + return section.warnings && section.warnings.length > 0; + }) ? "All systems operational, but performance might be degraded" : "All systems operational" : "Some issues have been detected"}
-
- Last check +
+ Last check {createDayString(healthStatus.time)}
- Version + Version {healthStatus.coder_version}
-
-
+
}> @@ -229,35 +207,3 @@ const filterVisibleSections = (sections: T) => { return visible; }; - -const classNames = { - link: (css, theme) => - css({ - background: "none", - pointerEvents: "auto", - color: theme.palette.text.secondary, - border: "none", - fontSize: 14, - width: "100%", - display: "flex", - alignItems: "center", - gap: 12, - textAlign: "left", - height: 36, - padding: "0 24px", - cursor: "pointer", - textDecoration: "none", - - "&:hover": { - background: theme.palette.action.hover, - color: theme.palette.text.primary, - }, - }), - - activeLink: (css, theme) => - css({ - background: theme.palette.action.hover, - pointerEvents: "none", - color: theme.palette.text.primary, - }), -} satisfies Record; From f92399587a712834add880cac8ee894af4ff96d6 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 22:32:00 +0000 Subject: [PATCH 04/11] deprecate `useClassName` --- site/src/hooks/useClassName.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/hooks/useClassName.ts b/site/src/hooks/useClassName.ts index 472e8681a028e..5155d1795a4a5 100644 --- a/site/src/hooks/useClassName.ts +++ b/site/src/hooks/useClassName.ts @@ -5,9 +5,9 @@ import { type DependencyList, useMemo } from "react"; export type ClassName = (cssFn: typeof css, theme: Theme) => string; /** - * An escape hatch for when you really need to manually pass around a - * `className`. Prefer using the `css` prop whenever possible. If you - * can't use that, then this might be helpful for you. + * @deprecated This hook was used as an escape hatch to generate class names + * using emotion when no other styling method would work. There is no valid new + * usage of this hook. Use Tailwind classes instead. */ export function useClassName(styles: ClassName, deps: DependencyList): string { const theme = useTheme(); From acf33a1232fd178d0f800c9762302272d631c459 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 23:00:36 +0000 Subject: [PATCH 05/11] format --- site/src/pages/HealthPage/HealthLayout.tsx | 24 ++++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index d7f5e2bfc9115..2c691ee2529b1 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -73,19 +73,11 @@ export const HealthLayout: FC = () => { -
-
-
+
+
+
-
+
@@ -116,10 +108,10 @@ export const HealthLayout: FC = () => { > {healthStatus.healthy ? Object.keys(visibleSections).some((key) => { - const section = - healthStatus[key as keyof typeof visibleSections]; - return section.warnings && section.warnings.length > 0; - }) + const section = + healthStatus[key as keyof typeof visibleSections]; + return section.warnings && section.warnings.length > 0; + }) ? "All systems operational, but performance might be degraded" : "All systems operational" : "Some issues have been detected"} From 4946c9a0a8ea3e07ae1dc75c9ef25c52afd7ec64 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 23:12:00 +0000 Subject: [PATCH 06/11] oh --- site/src/components/Sidebar/Sidebar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/Sidebar/Sidebar.tsx b/site/src/components/Sidebar/Sidebar.tsx index 249362788d2c0..f1dc776bdb6a6 100644 --- a/site/src/components/Sidebar/Sidebar.tsx +++ b/site/src/components/Sidebar/Sidebar.tsx @@ -46,7 +46,7 @@ export const SidebarHeader: FC = ({ ) : ( {title} )} - + {subtitle}
@@ -101,7 +101,7 @@ export const SidebarNavItem: FC = ({ "block relative text-sm text-inherit mb-px p-3 pl-4 rounded-sm", "transition-colors no-underline hover:bg-surface-secondary", isActive && - "bg-surface-secondary border-0 border-solid border-l-[3px] border-highlight-sky", + "bg-surface-secondary border-0 border-solid border-l-[3px] border-highlight-sky", ) } > From 709eb72146f43cdf69232ea90b451228b424bdfd Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 13 Aug 2025 23:15:02 +0000 Subject: [PATCH 07/11] >>>>>:( --- site/src/components/Sidebar/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/components/Sidebar/Sidebar.tsx b/site/src/components/Sidebar/Sidebar.tsx index f1dc776bdb6a6..a09d9bfbaa517 100644 --- a/site/src/components/Sidebar/Sidebar.tsx +++ b/site/src/components/Sidebar/Sidebar.tsx @@ -101,7 +101,7 @@ export const SidebarNavItem: FC = ({ "block relative text-sm text-inherit mb-px p-3 pl-4 rounded-sm", "transition-colors no-underline hover:bg-surface-secondary", isActive && - "bg-surface-secondary border-0 border-solid border-l-[3px] border-highlight-sky", + "bg-surface-secondary border-0 border-solid border-l-[3px] border-highlight-sky", ) } > From 29cb9ba70e945ca4cf20adfb54ea52ae88400708 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 14 Aug 2025 20:44:48 +0000 Subject: [PATCH 08/11] fix classnames --- .../GlobalSnackbar/EnterpriseSnackbar.tsx | 2 +- site/src/pages/HealthPage/HealthLayout.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx b/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx index 17036fb2be8e4..04b214995c814 100644 --- a/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx +++ b/site/src/components/GlobalSnackbar/EnterpriseSnackbar.tsx @@ -70,7 +70,7 @@ export const EnterpriseSnackbar: FC = ({ const variantColor = (variant: EnterpriseSnackbarVariant) => { switch (variant) { case "error": - return "border-highlight-red"; + return "border-border-destructive"; case "info": return "border-highlight-sky"; case "success": diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index 2c691ee2529b1..da97cdc81abd2 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -97,7 +97,7 @@ export const HealthLayout: FC = () => {
-
+
{healthStatus.healthy ? "Healthy" : "Unhealthy"}
{ > {healthStatus.healthy ? Object.keys(visibleSections).some((key) => { - const section = - healthStatus[key as keyof typeof visibleSections]; - return section.warnings && section.warnings.length > 0; - }) + const section = + healthStatus[key as keyof typeof visibleSections]; + return section.warnings && section.warnings.length > 0; + }) ? "All systems operational, but performance might be degraded" : "All systems operational" : "Some issues have been detected"} @@ -119,7 +119,7 @@ export const HealthLayout: FC = () => {
- Last check + Last check {
- Version + Version Date: Thu, 14 Aug 2025 20:49:44 +0000 Subject: [PATCH 09/11] hmm --- site/src/pages/HealthPage/HealthLayout.tsx | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index da97cdc81abd2..1acf667713ac3 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -19,6 +19,15 @@ import { createDayString } from "utils/createDayString"; import { pageTitle } from "utils/page"; import { HealthIcon } from "./Content"; +const linkStyles = { + normal: ` + text-content-secondary border-none text-sm w-full flex items-center gap-3 + text-left h-9 px-6 cursor-pointer no-underline transition-colors + hover:bg-surface-secondary hover:text-content-primary + ` + active: "bg-surface-secondary text-content-primary", +}; + export const HealthLayout: FC = () => { const theme = useTheme(); const queryClient = useQueryClient(); @@ -43,13 +52,6 @@ export const HealthLayout: FC = () => { } as const; const visibleSections = filterVisibleSections(sections); - const link = ` - text-content-secondary border-none text-sm w-full flex items-center gap-3 - text-left h-9 px-6 cursor-pointer no-underline transition-colors - hover:bg-surface-secondary hover:text-content-primary - `; - const activeLink = "bg-surface-secondary text-content-primary"; - if (isLoading) { return (
@@ -101,10 +103,7 @@ export const HealthLayout: FC = () => { {healthStatus.healthy ? "Healthy" : "Unhealthy"}
{healthStatus.healthy ? Object.keys(visibleSections).some((key) => { @@ -128,7 +127,7 @@ export const HealthLayout: FC = () => {
-
+
Version { key={key} to={`/health/${kebabCase(key)}`} className={({ isActive }) => - cn(link, isActive && activeLink) + cn(linkStyles.normal, isActive && linkStyles.active) } > Date: Thu, 14 Aug 2025 21:40:59 +0000 Subject: [PATCH 10/11] :| --- site/src/pages/HealthPage/HealthLayout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index 1acf667713ac3..82278bbb2044d 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -24,7 +24,7 @@ const linkStyles = { text-content-secondary border-none text-sm w-full flex items-center gap-3 text-left h-9 px-6 cursor-pointer no-underline transition-colors hover:bg-surface-secondary hover:text-content-primary - ` + `, active: "bg-surface-secondary text-content-primary", }; From 53b5fb1687c74bf027facaaf012d41e14599a49d Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 14 Aug 2025 21:42:56 +0000 Subject: [PATCH 11/11] come oooooon --- site/src/pages/HealthPage/HealthLayout.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index 82278bbb2044d..86e79aa9ec69e 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -102,15 +102,13 @@ export const HealthLayout: FC = () => {
{healthStatus.healthy ? "Healthy" : "Unhealthy"}
-
+
{healthStatus.healthy ? Object.keys(visibleSections).some((key) => { - const section = - healthStatus[key as keyof typeof visibleSections]; - return section.warnings && section.warnings.length > 0; - }) + const section = + healthStatus[key as keyof typeof visibleSections]; + return section.warnings && section.warnings.length > 0; + }) ? "All systems operational, but performance might be degraded" : "All systems operational" : "Some issues have been detected"}