From 185400db11bf82455ccc4cbff676021abb5e01a8 Mon Sep 17 00:00:00 2001
From: Kayla Washburn
Date: Mon, 4 Dec 2023 11:09:04 -0800
Subject: [PATCH] refactor: remove usage of `` and `sx` (#10702)
---
site/.eslintrc.yaml | 2 +
site/package.json | 1 -
site/pnpm-lock.yaml | 109 +------
site/src/@types/emotion.d.ts | 7 +-
site/src/@types/mui.d.ts | 10 +-
site/src/App.tsx | 10 +-
.../ActiveUserChart/ActiveUserChart.tsx | 16 +-
site/src/components/Alert/Alert.tsx | 24 +-
site/src/components/Avatar/Avatar.tsx | 13 +-
.../components/Dashboard/DashboardLayout.tsx | 43 +--
.../Dashboard/Navbar/NavbarView.tsx | 264 +++++++++--------
.../Navbar/UserDropdown/UserDropdown.tsx | 2 +-
.../Dashboard/useUpdateCheck.test.tsx | 6 +-
.../DeploySettingsLayout/Option.tsx | 107 +++----
.../DeploySettingsLayout/OptionsTable.tsx | 17 +-
site/src/components/EmptyState/EmptyState.tsx | 26 +-
site/src/components/Filter/UserFilter.tsx | 18 +-
site/src/components/Filter/filter.tsx | 276 ++++++++++--------
.../GlobalSnackbar/GlobalSnackbar.tsx | 51 ++--
site/src/components/LastSeen/LastSeen.tsx | 20 +-
site/src/components/Loader/Loader.tsx | 30 +-
site/src/components/OverflowY/OverflowY.tsx | 27 +-
site/src/components/Paywall/Paywall.tsx | 23 +-
site/src/components/Popover/Popover.tsx | 40 ++-
.../ProxyStatusLatency/ProxyStatusLatency.tsx | 22 +-
site/src/components/Resources/AgentButton.tsx | 21 +-
.../components/Resources/AgentMetadata.tsx | 59 ++--
.../Resources/PortForwardButton.stories.tsx | 15 +-
.../Resources/PortForwardButton.tsx | 2 +-
.../VSCodeDesktopButton.tsx | 16 +-
.../RichParameterInput/RichParameterInput.tsx | 7 +-
site/src/components/Stats/Stats.tsx | 30 +-
site/src/components/TableEmpty/TableEmpty.tsx | 9 +-
.../components/TableLoader/TableLoader.tsx | 21 +-
site/src/components/Tabs/Tabs.tsx | 10 +-
.../WorkspaceBuildLogs/WorkspaceBuildLogs.tsx | 29 +-
.../DormantWorkspaceBanner.tsx | 23 +-
site/src/pages/AuditPage/AuditFilter.tsx | 15 +-
.../pages/CreateUserPage/CreateUserForm.tsx | 2 +-
.../useWorkspaceDuplication.test.tsx | 2 +-
.../GeneralSettingsPageView.tsx | 13 +-
.../ExternalAuthPage/ExternalAuthPage.tsx | 5 +-
site/src/pages/GroupsPage/GroupPage.tsx | 5 +-
site/src/pages/GroupsPage/GroupsPageView.tsx | 7 +-
site/src/pages/HealthPage/HealthPage.tsx | 107 ++++---
site/src/pages/IconsPage/IconsPage.tsx | 9 +-
site/src/pages/LoginPage/OAuthSignInForm.tsx | 23 +-
.../TemplateEmbedPage/TemplateEmbedPage.tsx | 57 ++--
.../TemplateInsightsPage/IntervalMenu.tsx | 16 +-
.../TemplateInsightsPage/WeekPicker.tsx | 16 +-
.../TemplateVersionsPage/VersionsTable.tsx | 19 +-
.../UserOrGroupAutocomplete.tsx | 21 +-
.../AutostopRequirementHelperText.tsx | 26 +-
.../TemplateSchedulePage/ScheduleDialog.tsx | 20 +-
.../TemplateVersionEditor.tsx | 21 +-
.../pages/TemplatesPage/TemplatesPageView.tsx | 15 +-
.../src/pages/TerminalPage/TerminalAlerts.tsx | 25 +-
site/src/pages/TerminalPage/TerminalPage.tsx | 69 +++--
.../pages/TerminalPage/TerminalPageAlert.tsx | 139 ---------
.../AccountPage/AccountPage.tsx | 10 +-
.../AccountPage/AccountUserGroups.tsx | 12 +-
.../SSHKeysPage/SSHKeysPageView.tsx | 5 +-
.../SecurityPage/SecurityPage.tsx | 10 +-
.../SecurityPage/SingleSignOnSection.tsx | 76 ++---
.../TokensPage/TokensPageView.tsx | 11 +-
.../WorkspaceProxyPage/WorkspaceProxyRow.tsx | 102 +++----
.../WorkspaceProxyPage/WorkspaceProxyView.tsx | 2 +-
site/src/pages/UsersPage/UsersFilter.tsx | 58 ++--
.../UsersPage/UsersTable/UserGroupsCell.tsx | 2 +-
.../UsersPage/UsersTable/UsersTableBody.tsx | 70 ++---
site/src/pages/WorkspacePage/BuildsTable.tsx | 22 +-
.../BuildParametersPopover.tsx | 82 +++---
.../WorkspaceActions/Buttons.tsx | 71 ++---
.../WorkspaceBuildLogsSection.tsx | 52 ++--
.../src/pages/WorkspacePage/WorkspacePage.tsx | 5 +-
.../WorkspaceParametersPage.tsx | 12 +-
.../src/pages/WorkspacesPage/BatchActions.tsx | 34 ++-
.../pages/WorkspacesPage/WorkspacesButton.tsx | 48 +--
.../WorkspacesPage/WorkspacesPageView.tsx | 5 +-
.../WorkspacesPage/WorkspacesSearchBox.tsx | 68 +++--
.../pages/WorkspacesPage/WorkspacesTable.tsx | 78 ++---
.../pages/WorkspacesPage/filter/filter.tsx | 77 ++---
.../pages/WorkspacesPage/filter/options.ts | 3 +-
site/src/testHelpers/renderHelpers.tsx | 8 +-
84 files changed, 1440 insertions(+), 1521 deletions(-)
delete mode 100644 site/src/pages/TerminalPage/TerminalPageAlert.tsx
diff --git a/site/.eslintrc.yaml b/site/.eslintrc.yaml
index 0991916c76..e2d919bcd8 100644
--- a/site/.eslintrc.yaml
+++ b/site/.eslintrc.yaml
@@ -142,6 +142,8 @@ rules:
- name: "@mui/material/Typography"
message:
"You should use the native HTML elements as span, p, h1, h2, h3..."
+ - name: "@mui/material/Box"
+ message: "You should use a instead"
no-unused-vars: "off"
"object-curly-spacing": "off"
react-hooks/exhaustive-deps: warn
diff --git a/site/package.json b/site/package.json
index 8b6c556224..8943a338ea 100644
--- a/site/package.json
+++ b/site/package.json
@@ -40,7 +40,6 @@
"@mui/icons-material": "5.14.0",
"@mui/lab": "5.0.0-alpha.129",
"@mui/material": "5.14.0",
- "@mui/styles": "5.14.0",
"@mui/system": "5.14.0",
"@mui/utils": "5.14.11",
"@vitejs/plugin-react": "4.1.0",
diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml
index 53e16f6e0c..a923fcc904 100644
--- a/site/pnpm-lock.yaml
+++ b/site/pnpm-lock.yaml
@@ -45,9 +45,6 @@ dependencies:
'@mui/material':
specifier: 5.14.0
version: 5.14.0(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
- '@mui/styles':
- specifier: 5.14.0
- version: 5.14.0(@types/react@18.2.6)(react@18.2.0)
'@mui/system':
specifier: 5.14.0
version: 5.14.0(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0)
@@ -3096,37 +3093,6 @@ packages:
react: 18.2.0
dev: false
- /@mui/styles@5.14.0(@types/react@18.2.6)(react@18.2.0):
- resolution: {integrity: sha512-+nXPk/7qhlJn2QGSBlB42gM9G4shLDEAfkTqjUoCDsS3qPo7ZlpM2X5SgnCNoYhXCn820R0YxaJYd19rmC3FSA==}
- engines: {node: '>=12.0.0'}
- peerDependencies:
- '@types/react': ^17.0.0 || ^18.0.0
- react: ^17.0.0
- peerDependenciesMeta:
- '@types/react':
- optional: true
- dependencies:
- '@babel/runtime': 7.22.6
- '@emotion/hash': 0.9.1
- '@mui/private-theming': 5.13.7(@types/react@18.2.6)(react@18.2.0)
- '@mui/types': 7.2.4(@types/react@18.2.6)
- '@mui/utils': 5.14.11(@types/react@18.2.6)(react@18.2.0)
- '@types/react': 18.2.6
- clsx: 1.2.1
- csstype: 3.1.2
- hoist-non-react-statics: 3.3.2
- jss: 10.10.0
- jss-plugin-camel-case: 10.10.0
- jss-plugin-default-unit: 10.10.0
- jss-plugin-global: 10.10.0
- jss-plugin-nested: 10.10.0
- jss-plugin-props-sort: 10.10.0
- jss-plugin-rule-value-function: 10.10.0
- jss-plugin-vendor-prefixer: 10.10.0
- prop-types: 15.8.1
- react: 18.2.0
- dev: false
-
/@mui/system@5.14.0(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0):
resolution: {integrity: sha512-0HZGkX8miJbiNw+rjlZ9l0Cfkz1bSqfSHQH0EH9J+nx0aAm5cBleg9piOlLdCNIWGgecCqsw4x62erGrGjjcJg==}
engines: {node: '>=12.0.0'}
@@ -7218,13 +7184,6 @@ packages:
source-map: 0.6.1
dev: false
- /css-vendor@2.0.8:
- resolution: {integrity: sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==}
- dependencies:
- '@babel/runtime': 7.23.2
- is-in-browser: 1.1.3
- dev: false
-
/css.escape@1.5.1:
resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
dev: true
@@ -9252,10 +9211,6 @@ packages:
resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==}
dev: false
- /is-in-browser@1.1.3:
- resolution: {integrity: sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==}
- dev: false
-
/is-interactive@1.0.0:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
engines: {node: '>=8'}
@@ -10174,68 +10129,6 @@ packages:
graceful-fs: 4.2.11
dev: true
- /jss-plugin-camel-case@10.10.0:
- resolution: {integrity: sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==}
- dependencies:
- '@babel/runtime': 7.23.2
- hyphenate-style-name: 1.0.4
- jss: 10.10.0
- dev: false
-
- /jss-plugin-default-unit@10.10.0:
- resolution: {integrity: sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==}
- dependencies:
- '@babel/runtime': 7.23.2
- jss: 10.10.0
- dev: false
-
- /jss-plugin-global@10.10.0:
- resolution: {integrity: sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==}
- dependencies:
- '@babel/runtime': 7.23.2
- jss: 10.10.0
- dev: false
-
- /jss-plugin-nested@10.10.0:
- resolution: {integrity: sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==}
- dependencies:
- '@babel/runtime': 7.23.2
- jss: 10.10.0
- tiny-warning: 1.0.3
- dev: false
-
- /jss-plugin-props-sort@10.10.0:
- resolution: {integrity: sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==}
- dependencies:
- '@babel/runtime': 7.23.2
- jss: 10.10.0
- dev: false
-
- /jss-plugin-rule-value-function@10.10.0:
- resolution: {integrity: sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==}
- dependencies:
- '@babel/runtime': 7.23.2
- jss: 10.10.0
- tiny-warning: 1.0.3
- dev: false
-
- /jss-plugin-vendor-prefixer@10.10.0:
- resolution: {integrity: sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==}
- dependencies:
- '@babel/runtime': 7.23.2
- css-vendor: 2.0.8
- jss: 10.10.0
- dev: false
-
- /jss@10.10.0:
- resolution: {integrity: sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==}
- dependencies:
- '@babel/runtime': 7.23.2
- csstype: 3.1.2
- is-in-browser: 1.1.3
- tiny-warning: 1.0.3
- dev: false
-
/jsx-ast-utils@3.3.4:
resolution: {integrity: sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw==}
engines: {node: '>=4.0'}
@@ -14244,7 +14137,7 @@ packages:
peerDependencies:
eslint: '>=7'
meow: ^9.0.0
- optionator: 0.9.3
+ optionator: ^0.9.1
stylelint: '>=13'
typescript: '*'
vite: '>=2.0.0'
diff --git a/site/src/@types/emotion.d.ts b/site/src/@types/emotion.d.ts
index 1bb431a7e2..3e6adf9cd8 100644
--- a/site/src/@types/emotion.d.ts
+++ b/site/src/@types/emotion.d.ts
@@ -1,8 +1,5 @@
-import type { DefaultTheme as MuiTheme } from "@mui/system";
-import type { NewTheme } from "theme/experimental";
+import type { Theme as MuiTheme } from "@mui/material/styles";
declare module "@emotion/react" {
- interface Theme extends MuiTheme {
- experimental: NewTheme;
- }
+ interface Theme extends MuiTheme {}
}
diff --git a/site/src/@types/mui.d.ts b/site/src/@types/mui.d.ts
index 50ca165c18..17abc0aa2d 100644
--- a/site/src/@types/mui.d.ts
+++ b/site/src/@types/mui.d.ts
@@ -1,14 +1,6 @@
-import type {
- PaletteColor,
- PaletteColorOptions,
- Theme,
-} from "@mui/material/styles";
+import type { PaletteColor, PaletteColorOptions } from "@mui/material/styles";
import type { NewTheme } from "theme/experimental";
-declare module "@mui/styles/defaultTheme" {
- interface DefaultTheme extends Theme {}
-}
-
declare module "@mui/material/styles" {
interface Theme {
experimental: NewTheme;
diff --git a/site/src/App.tsx b/site/src/App.tsx
index da700660d7..7fa09d271d 100644
--- a/site/src/App.tsx
+++ b/site/src/App.tsx
@@ -48,12 +48,14 @@ export const ThemeProviders: FC
= ({ children }) => {
);
};
-export const AppProviders = ({
- children,
- queryClient = defaultQueryClient,
-}: {
+interface AppProvidersProps {
children: ReactNode;
queryClient?: QueryClient;
+}
+
+export const AppProviders: FC = ({
+ children,
+ queryClient = defaultQueryClient,
}) => {
return (
diff --git a/site/src/components/ActiveUserChart/ActiveUserChart.tsx b/site/src/components/ActiveUserChart/ActiveUserChart.tsx
index 15ff04864c..8541297819 100644
--- a/site/src/components/ActiveUserChart/ActiveUserChart.tsx
+++ b/site/src/components/ActiveUserChart/ActiveUserChart.tsx
@@ -1,6 +1,3 @@
-import Box from "@mui/material/Box";
-import { Theme } from "@mui/material/styles";
-import useTheme from "@mui/styles/useTheme";
import {
CategoryScale,
Chart as ChartJS,
@@ -22,7 +19,8 @@ import {
HelpTooltipText,
} from "components/HelpTooltip/HelpTooltip";
import dayjs from "dayjs";
-import { FC } from "react";
+import { useTheme } from "@emotion/react";
+import { type FC } from "react";
import { Line } from "react-chartjs-2";
import annotationPlugin from "chartjs-plugin-annotation";
@@ -42,7 +40,7 @@ ChartJS.register(
const USER_LIMIT_DISPLAY_THRESHOLD = 60;
export interface ActiveUserChartProps {
- data: { date: string; amount: number }[];
+ data: Array<{ date: string; amount: number }>;
interval: "day" | "week";
userLimit: number | undefined;
}
@@ -52,7 +50,7 @@ export const ActiveUserChart: FC = ({
interval,
userLimit,
}) => {
- const theme: Theme = useTheme();
+ const theme = useTheme();
const labels = data.map((val) => dayjs(val.date).format("YYYY-MM-DD"));
const chartData = data.map((val) => val.amount);
@@ -137,9 +135,9 @@ export const ActiveUserChart: FC = ({
);
};
-export const ActiveUsersTitle = () => {
+export const ActiveUsersTitle: FC = () => {
return (
-
+
Active Users
How do we calculate active users?
@@ -148,7 +146,7 @@ export const ActiveUsersTitle = () => {
considered an active user. e.g. apps, web terminal, SSH
-
+
);
};
diff --git a/site/src/components/Alert/Alert.tsx b/site/src/components/Alert/Alert.tsx
index bba4044c4c..6191de2c55 100644
--- a/site/src/components/Alert/Alert.tsx
+++ b/site/src/components/Alert/Alert.tsx
@@ -1,9 +1,15 @@
-import { useState, FC, ReactNode } from "react";
+import {
+ useState,
+ type FC,
+ type ReactNode,
+ type PropsWithChildren,
+} from "react";
import Collapse from "@mui/material/Collapse";
// eslint-disable-next-line no-restricted-imports -- It is the base component
-import MuiAlert, { AlertProps as MuiAlertProps } from "@mui/material/Alert";
+import MuiAlert, {
+ type AlertProps as MuiAlertProps,
+} from "@mui/material/Alert";
import Button from "@mui/material/Button";
-import Box from "@mui/material/Box";
export type AlertProps = MuiAlertProps & {
actions?: ReactNode;
@@ -32,7 +38,7 @@ export const Alert: FC = ({
@@ -62,15 +68,13 @@ export const Alert: FC = ({
);
};
-export const AlertDetail = ({ children }: { children: ReactNode }) => {
+export const AlertDetail: FC = ({ children }) => {
return (
- theme.palette.text.secondary}
- fontSize={13}
+ ({ color: theme.palette.text.secondary, fontSize: 13 })}
data-chromatic="ignore"
>
{children}
-
+
);
};
diff --git a/site/src/components/Avatar/Avatar.tsx b/site/src/components/Avatar/Avatar.tsx
index f47f85e326..ebc234e848 100644
--- a/site/src/components/Avatar/Avatar.tsx
+++ b/site/src/components/Avatar/Avatar.tsx
@@ -1,9 +1,10 @@
// This is the only place MuiAvatar can be used
// eslint-disable-next-line no-restricted-imports -- Read above
-import MuiAvatar, { AvatarProps as MuiAvatarProps } from "@mui/material/Avatar";
-import { FC, useId } from "react";
+import MuiAvatar, {
+ type AvatarProps as MuiAvatarProps,
+} from "@mui/material/Avatar";
+import { type FC, useId } from "react";
import { css, type Interpolation, type Theme } from "@emotion/react";
-import { Box } from "@mui/system";
import { visuallyHidden } from "@mui/utils";
export type AvatarProps = MuiAvatarProps & {
@@ -78,6 +79,8 @@ 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 (
<>
= ({ src, alt }) => {
css={{ maxWidth: "50%" }}
aria-labelledby={avatarId}
/>
-
+
{alt}
-
+
>
);
};
diff --git a/site/src/components/Dashboard/DashboardLayout.tsx b/site/src/components/Dashboard/DashboardLayout.tsx
index e06888c6b3..35cc12611e 100644
--- a/site/src/components/Dashboard/DashboardLayout.tsx
+++ b/site/src/components/Dashboard/DashboardLayout.tsx
@@ -1,18 +1,17 @@
-import { DeploymentBanner } from "./DeploymentBanner/DeploymentBanner";
+import Snackbar from "@mui/material/Snackbar";
+import Link from "@mui/material/Link";
+import Button from "@mui/material/Button";
+import InfoOutlined from "@mui/icons-material/InfoOutlined";
+import { type FC, type HTMLAttributes, Suspense } from "react";
+import { Outlet } from "react-router-dom";
import { LicenseBanner } from "components/Dashboard/LicenseBanner/LicenseBanner";
import { Loader } from "components/Loader/Loader";
import { ServiceBanner } from "components/Dashboard/ServiceBanner/ServiceBanner";
import { usePermissions } from "hooks/usePermissions";
-import { FC, Suspense } from "react";
-import { Outlet } from "react-router-dom";
import { dashboardContentBottomPadding } from "theme/constants";
-import { Navbar } from "./Navbar/Navbar";
-import Snackbar from "@mui/material/Snackbar";
-import Link from "@mui/material/Link";
-import Box, { BoxProps } from "@mui/material/Box";
-import InfoOutlined from "@mui/icons-material/InfoOutlined";
-import Button from "@mui/material/Button";
import { docs } from "utils/docs";
+import { Navbar } from "./Navbar/Navbar";
+import { DeploymentBanner } from "./DeploymentBanner/DeploymentBanner";
import { useUpdateCheck } from "./useUpdateCheck";
export const DashboardLayout: FC = () => {
@@ -74,21 +73,21 @@ export const DashboardLayout: FC = () => {
}),
}}
message={
-
+
({
+ css={(theme) => ({
fontSize: 16,
height: 20, // 20 is the height of the text line so we can align them
color: theme.palette.info.light,
})}
/>
-
+
Coder {updateCheck.data?.version} is now available. View the{" "}
release notes and{" "}
upgrade instructions{" "}
for more information.
-
-
+
+
}
action={
@@ -101,12 +100,14 @@ export const DashboardLayout: FC = () => {
);
};
-export const DashboardFullPage = (props: BoxProps) => {
+export const DashboardFullPage: FC> = ({
+ children,
+ ...attrs
+}) => {
return (
- {
flexBasis: 0,
minHeight: "100%",
}}
- />
+ >
+ {children}
+
);
};
diff --git a/site/src/components/Dashboard/Navbar/NavbarView.tsx b/site/src/components/Dashboard/Navbar/NavbarView.tsx
index f23963bfbb..2ef95770a8 100644
--- a/site/src/components/Dashboard/Navbar/NavbarView.tsx
+++ b/site/src/components/Dashboard/Navbar/NavbarView.tsx
@@ -2,7 +2,6 @@ import Drawer from "@mui/material/Drawer";
import IconButton from "@mui/material/IconButton";
import Divider from "@mui/material/Divider";
import Skeleton from "@mui/material/Skeleton";
-import Box from "@mui/material/Box";
import Menu from "@mui/material/Menu";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
@@ -45,81 +44,6 @@ export const Language = {
deployment: "Deployment",
};
-const styles = {
- desktopNavItems: (theme) => css`
- display: none;
-
- ${theme.breakpoints.up("md")} {
- display: flex;
- }
- `,
- mobileMenuButton: (theme) => css`
- ${theme.breakpoints.up("md")} {
- display: none;
- }
- `,
- wrapper: (theme) => css`
- position: relative;
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- ${theme.breakpoints.up("md")} {
- justify-content: flex-start;
- }
- `,
- drawerHeader: {
- padding: 16,
- paddingTop: 32,
- paddingBottom: 32,
- },
- logo: (theme) => css`
- align-items: center;
- display: flex;
- height: ${navHeight}px;
- color: ${theme.palette.text.primary};
- padding: 16px;
-
- // svg is for the Coder logo, img is for custom images
- & svg,
- & img {
- height: 100%;
- object-fit: contain;
- }
- `,
- drawerLogo: {
- padding: 0,
- maxHeight: 40,
- },
- item: {
- padding: 0,
- },
- link: (theme) => css`
- align-items: center;
- color: ${colors.gray[6]};
- display: flex;
- flex: 1;
- font-size: 16px;
- padding: 12px 16px;
- text-decoration: none;
- transition: background-color 0.15s ease-in-out;
-
- &.active {
- color: ${theme.palette.text.primary};
- font-weight: 500;
- }
-
- &:hover {
- background-color: ${theme.palette.action.hover};
- }
-
- ${theme.breakpoints.up("md")} {
- height: ${navHeight}px;
- padding: 0 24px;
- }
- `,
-} satisfies Record>;
-
interface NavItemsProps {
children?: ReactNode;
className?: string;
@@ -128,9 +52,12 @@ interface NavItemsProps {
canViewAllUsers: boolean;
}
-const NavItems: React.FC = (props) => {
- const { className, canViewAuditLog, canViewDeployment, canViewAllUsers } =
- props;
+const NavItems: FC = ({
+ className,
+ canViewAuditLog,
+ canViewDeployment,
+ canViewAllUsers,
+}) => {
const location = useLocation();
const theme = useTheme();
@@ -181,15 +108,16 @@ export const NavbarView: FC = ({
canViewAllUsers,
proxyContextValue,
}) => {
+ const theme = useTheme();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
return (
({
+ css={{
height: navHeight,
borderBottom: `1px solid ${theme.palette.divider}`,
backgroundColor: theme.palette.background.paper,
- })}
+ }}
>
= ({
canViewAllUsers={canViewAllUsers}
/>
-
+
{proxyContextValue && (
)}
@@ -259,15 +181,18 @@ export const NavbarView: FC
= ({
onSignOut={onSignOut}
/>
)}
-
+
);
};
-const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
- proxyContextValue,
-}) => {
+interface ProxyMenuProps {
+ proxyContextValue: ProxyContextValue;
+}
+
+const ProxyMenu: FC = ({ proxyContextValue }) => {
+ const theme = useTheme();
const buttonRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const [refetchDate, setRefetchDate] = useState();
@@ -305,7 +230,7 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
);
}
@@ -317,28 +242,29 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
onClick={() => setIsOpen(true)}
size="small"
endIcon={ }
- sx={{
+ css={{
borderRadius: "999px",
"& .MuiSvgIcon-root": { fontSize: 14 },
}}
>
{selectedProxy ? (
-
-
-
+
+
-
+
-
+
) : (
"Select Proxy"
)}
@@ -348,13 +274,13 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
anchorEl={buttonRef.current}
onClick={closeMenu}
onClose={closeMenu}
- sx={{ "& .MuiMenu-paper": { py: 1 } }}
+ css={{ "& .MuiMenu-paper": { paddingTop: 8, paddingBottom: 8 } }}
>
- = ({
manually selected, otherwise the default primary region will be
used.
-
- theme.palette.divider }} />
+
+
{proxyContextValue.proxies
?.sort((a, b) => {
const latencyA = latencies?.[a.id]?.latencyMS ?? Infinity;
@@ -405,33 +331,39 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
}}
key={proxy.id}
selected={proxy.id === selectedProxy?.id}
- sx={{
- fontSize: 14,
- }}
+ css={{ fontSize: 14 }}
>
-
-
-
+
+
-
+
{proxy.display_name}
-
+
))}
- theme.palette.divider }} />
+
{Boolean(permissions.editWorkspaceProxies) && (
{
navigate("deployment/workspace-proxies");
}}
@@ -440,7 +372,7 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
)}
{
// Stop the menu from closing
e.stopPropagation();
@@ -455,3 +387,85 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({
>
);
};
+
+const styles = {
+ desktopNavItems: (theme) => css`
+ display: none;
+
+ ${theme.breakpoints.up("md")} {
+ display: flex;
+ }
+ `,
+ mobileMenuButton: (theme) => css`
+ ${theme.breakpoints.up("md")} {
+ display: none;
+ }
+ `,
+ navMenus: (theme) => ({
+ display: "flex",
+ gap: 16,
+ alignItems: "center",
+ paddingRight: 16,
+
+ [theme.breakpoints.up("md")]: {
+ marginLeft: "auto",
+ },
+ }),
+ wrapper: (theme) => css`
+ position: relative;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ ${theme.breakpoints.up("md")} {
+ justify-content: flex-start;
+ }
+ `,
+ drawerHeader: {
+ padding: 16,
+ paddingTop: 32,
+ paddingBottom: 32,
+ },
+ logo: (theme) => css`
+ align-items: center;
+ display: flex;
+ height: ${navHeight}px;
+ color: ${theme.palette.text.primary};
+ padding: 16px;
+
+ // svg is for the Coder logo, img is for custom images
+ & svg,
+ & img {
+ height: 100%;
+ object-fit: contain;
+ }
+ `,
+ drawerLogo: {
+ padding: 0,
+ maxHeight: 40,
+ },
+ link: (theme) => css`
+ align-items: center;
+ color: ${colors.gray[6]};
+ display: flex;
+ flex: 1;
+ font-size: 16px;
+ padding: 12px 16px;
+ text-decoration: none;
+ transition: background-color 0.15s ease-in-out;
+
+ &.active {
+ color: ${theme.palette.text.primary};
+ font-weight: 500;
+ }
+
+ &:hover {
+ background-color: ${theme.palette.action.hover};
+ }
+
+ ${theme.breakpoints.up("md")} {
+ height: ${navHeight}px;
+ padding: 0 24px;
+ }
+ `,
+} satisfies Record>;
diff --git a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx
index a1c5cba6ad..b7a025d2ff 100644
--- a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx
+++ b/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx
@@ -57,7 +57,7 @@ export const UserDropdown: FC> = ({
>
{
+const createWrapper = (): FC => {
const queryClient = new QueryClient();
- return ({ children }: { children: ReactNode }) => (
+ return ({ children }) => (
{children}
);
};
diff --git a/site/src/components/DeploySettingsLayout/Option.tsx b/site/src/components/DeploySettingsLayout/Option.tsx
index 978233acce..6db042e7f0 100644
--- a/site/src/components/DeploySettingsLayout/Option.tsx
+++ b/site/src/components/DeploySettingsLayout/Option.tsx
@@ -1,18 +1,14 @@
-import Box, { type BoxProps } from "@mui/material/Box";
import CheckCircleOutlined from "@mui/icons-material/CheckCircleOutlined";
import { css, useTheme } from "@emotion/react";
-import type { PropsWithChildren, FC } from "react";
+import type { HTMLAttributes, PropsWithChildren, FC } from "react";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
import { DisabledBadge, EnabledBadge } from "../Badges/Badges";
-export const OptionName: FC = (props) => {
- const { children } = props;
-
+export const OptionName: FC = ({ children }) => {
return {children} ;
};
-export const OptionDescription: FC = (props) => {
- const { children } = props;
+export const OptionDescription: FC = ({ children }) => {
const theme = useTheme();
return (
@@ -37,31 +33,20 @@ export const OptionValue: FC = (props) => {
const { children: value } = props;
const theme = useTheme();
- const optionStyles = css`
- font-size: 14px;
- font-family: ${MONOSPACE_FONT_FAMILY};
- overflow-wrap: anywhere;
- user-select: all;
-
- & ul {
- padding: 16px;
- }
- `;
-
if (typeof value === "boolean") {
return value ? : ;
}
if (typeof value === "number") {
- return {value} ;
+ return {value} ;
}
if (!value || value.length === 0) {
- return Not set ;
+ return Not set ;
}
if (typeof value === "string") {
- return {value} ;
+ return {value} ;
}
if (typeof value === "object" && !Array.isArray(value)) {
@@ -73,31 +58,31 @@ export const OptionValue: FC = (props) => {
-
{isEnabled && (
({
width: 16,
height: 16,
- color: (theme) => theme.palette.success.light,
+ color: theme.palette.success.light,
margin: "0 8px",
- }}
+ })}
/>
)}
{option}
-
+
))}
@@ -108,7 +93,7 @@ export const OptionValue: FC = (props) => {
return (
{value.map((item) => (
-
+
{item}
))}
@@ -116,63 +101,83 @@ export const OptionValue: FC = (props) => {
);
}
- return {JSON.stringify(value)} ;
+ return {JSON.stringify(value)} ;
};
-interface OptionConfigProps extends BoxProps {
+interface OptionConfigProps extends HTMLAttributes {
source?: boolean;
}
-// OptionalConfig takes a source bool to indicate if the Option is the source of the configured value.
-export const OptionConfig = (props: OptionConfigProps) => {
- const { source, sx, ...attrs } = props;
+// OptionConfig takes a source bool to indicate if the Option is the source of the configured value.
+export const OptionConfig: FC = ({
+ children,
+ source,
+ ...attrs
+}) => {
const theme = useTheme();
const borderColor = source ? undefined : theme.palette.divider;
return (
-
- source ? theme.palette.primary.dark : theme.palette.background.paper,
+ backgroundColor: source
+ ? theme.palette.primary.dark
+ : theme.palette.background.paper,
display: "inline-flex",
alignItems: "center",
- borderRadius: 0.25,
+ borderRadius: 2,
padding: "0 8px",
border: `1px solid ${borderColor}`,
- ...sx,
}}
- />
+ >
+ {children}
+
);
};
-interface OptionConfigFlagProps extends BoxProps {
+interface OptionConfigFlagProps extends HTMLAttributes {
source?: boolean;
}
-export const OptionConfigFlag = (props: OptionConfigFlagProps) => {
- const { children, source, sx, ...attrs } = props;
+export const OptionConfigFlag: FC = ({
+ children,
+ source,
+ ...attrs
+}) => {
+ const theme = useTheme();
return (
-
- source ? "rgba(0, 0, 0, 0.7)" : theme.palette.divider,
+ backgroundColor: source ? "rgba(0, 0, 0, 0.7)" : theme.palette.divider,
lineHeight: 1,
padding: "2px 4px",
- borderRadius: 0.25,
- ...sx,
+ borderRadius: 2,
}}
>
{children}
-
+
);
};
+
+const styles = {
+ option: css`
+ font-size: 14px;
+ font-family: ${MONOSPACE_FONT_FAMILY};
+ overflow-wrap: anywhere;
+ user-select: all;
+
+ & ul {
+ padding: 16px;
+ }
+ `,
+};
diff --git a/site/src/components/DeploySettingsLayout/OptionsTable.tsx b/site/src/components/DeploySettingsLayout/OptionsTable.tsx
index c36f97b9b5..57ae0729a3 100644
--- a/site/src/components/DeploySettingsLayout/OptionsTable.tsx
+++ b/site/src/components/DeploySettingsLayout/OptionsTable.tsx
@@ -5,7 +5,6 @@ import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import { type FC } from "react";
-import Box from "@mui/material/Box";
import { css } from "@emotion/react";
import type { ClibaseOption } from "api/typesGenerated";
import {
@@ -17,10 +16,12 @@ import {
} from "components/DeploySettingsLayout/Option";
import { optionValue } from "./optionValue";
-const OptionsTable: FC<{
+interface OptionsTableProps {
options: ClibaseOption[];
additionalValues?: string[];
-}> = ({ options, additionalValues }) => {
+}
+
+const OptionsTable: FC = ({ options, additionalValues }) => {
if (options.length === 0) {
return No options to configure
;
}
@@ -60,12 +61,12 @@ const OptionsTable: FC<{
{option.name}
{option.description}
-
{option.flag && (
@@ -92,7 +93,7 @@ const OptionsTable: FC<{
{option.yaml}
)}
-
+
diff --git a/site/src/components/EmptyState/EmptyState.tsx b/site/src/components/EmptyState/EmptyState.tsx
index 228b7c06e9..a36069db4c 100644
--- a/site/src/components/EmptyState/EmptyState.tsx
+++ b/site/src/components/EmptyState/EmptyState.tsx
@@ -1,11 +1,10 @@
-import Box from "@mui/material/Box";
import type { FC, ReactNode } from "react";
export interface EmptyStateProps {
/** Text Message to display, placed inside Typography component */
message: string;
/** Longer optional description to display below the message */
- description?: string | React.ReactNode;
+ description?: string | ReactNode;
cta?: ReactNode;
className?: string;
image?: ReactNode;
@@ -15,17 +14,16 @@ export interface EmptyStateProps {
* Component to place on screens or in lists that have no content. Optionally
* provide a button that would allow the user to return from where they were,
* or to add an item that they currently have none of.
- *
- * EmptyState's props extend the [Material UI Box component](https://material-ui.com/components/box/)
- * that you can directly pass props through to to customize the shape and layout of it.
*/
-export const EmptyState: FC> = (
- props,
-) => {
- const { message, description, cta, image, ...boxProps } = props;
-
+export const EmptyState: FC = ({
+ message,
+ description,
+ cta,
+ image,
+ ...attrs
+}) => {
return (
- > = (
padding: "80px 40px",
position: "relative",
}}
- {...boxProps}
+ {...attrs}
>
- {message}
+ {message}
{description && (
({
@@ -55,6 +53,6 @@ export const EmptyState: FC> = (
)}
{cta && {cta}
}
{image}
-
+
);
};
diff --git a/site/src/components/Filter/UserFilter.tsx b/site/src/components/Filter/UserFilter.tsx
index 61b7754e0f..14bab75b52 100644
--- a/site/src/components/Filter/UserFilter.tsx
+++ b/site/src/components/Filter/UserFilter.tsx
@@ -1,3 +1,4 @@
+import { type FC } from "react";
import { useMe } from "hooks";
import { BaseOption } from "./options";
import { getUsers } from "api/api";
@@ -67,7 +68,11 @@ export const useUserFilterMenu = ({
export type UserFilterMenu = ReturnType;
-export const UserMenu = (menu: UserFilterMenu) => {
+interface UserMenuProps {
+ menu: UserFilterMenu;
+}
+
+export const UserMenu: FC = ({ menu }) => {
return (
)}
@@ -371,17 +389,17 @@ const PresetMenu = ({
);
};
-export const FilterMenu = ({
- id,
- menu,
- label,
- children,
-}: {
+interface FilterMenuProps {
menu: ReturnType>;
label: ReactNode;
id: string;
children: (values: { option: TOption; isSelected: boolean }) => ReactNode;
-}) => {
+}
+
+export const FilterMenu = (
+ props: FilterMenuProps,
+) => {
+ const { id, menu, label, children } = props;
const buttonRef = useRef(null);
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -394,7 +412,7 @@ export const FilterMenu = ({
setIsMenuOpen(true)}
- sx={{ minWidth: 200 }}
+ css={{ minWidth: 200 }}
>
{label}
@@ -403,7 +421,7 @@ export const FilterMenu = ({
anchorEl={buttonRef.current}
open={isMenuOpen}
onClose={handleClose}
- sx={{ "& .MuiPaper-root": { minWidth: 200 } }}
+ css={{ "& .MuiPaper-root": { minWidth: 200 } }}
// Disabled this so when we clear the filter and do some sorting in the
// search items it does not look strange. Github removes exit transitions
// on their filters as well.
@@ -432,17 +450,19 @@ export const FilterMenu = ({
);
};
+interface FilterSearchMenuProps {
+ menu: ReturnType>;
+ label: ReactNode;
+ id: string;
+ children: (values: { option: TOption; isSelected: boolean }) => ReactNode;
+}
+
export const FilterSearchMenu = ({
id,
menu,
label,
children,
-}: {
- menu: ReturnType>;
- label: ReactNode;
- id: string;
- children: (values: { option: TOption; isSelected: boolean }) => ReactNode;
-}) => {
+}: FilterSearchMenuProps) => {
const buttonRef = useRef(null);
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -455,7 +475,7 @@ export const FilterSearchMenu = ({
setIsMenuOpen(true)}
- sx={{ minWidth: 200 }}
+ css={{ minWidth: 200 }}
>
{label}
@@ -493,57 +513,70 @@ type OptionItemProps = {
isSelected?: boolean;
};
-export const OptionItem = ({ option, left, isSelected }: OptionItemProps) => {
+export const OptionItem: FC = ({
+ option,
+ left,
+ isSelected,
+}) => {
return (
-
{left}
-
+
{option.label}
-
+
{isSelected && (
-
+
)}
-
+
);
};
const MenuButton = forwardRef((props, ref) => {
+ const { children, ...attrs } = props;
+
return (
}
- {...props}
- sx={{
+ css={{
borderRadius: "6px",
justifyContent: "space-between",
lineHeight: "120%",
- ...props.sx,
}}
- />
+ {...attrs}
+ >
+ {children}
+
);
});
-function SearchMenu({
+interface SearchMenuProps
+ extends Pick {
+ options?: TOption[];
+ renderOption: (option: TOption) => ReactNode;
+ query: string;
+ onQueryChange: (query: string) => void;
+}
+
+function SearchMenu({
options,
renderOption,
query,
onQueryChange,
...menuProps
-}: Pick & {
- options?: TOption[];
- renderOption: (option: TOption) => ReactNode;
- query: string;
- onQueryChange: (query: string) => void;
-}) {
+}: SearchMenuProps) {
const menuListRef = useRef(null);
const searchInputRef = useRef(null);
+ const theme = useTheme();
return (
({
menuProps.onClose && menuProps.onClose(event, reason);
onQueryChange("");
}}
- sx={{
+ css={{
"& .MuiPaper-root": {
width: 320,
paddingY: 0,
@@ -565,33 +598,31 @@ function SearchMenu({
enter: 250,
exit: 0,
}}
+ onKeyDown={(e) => {
+ e.stopPropagation();
+ if (e.key === "ArrowDown" && menuListRef.current) {
+ const firstItem = menuListRef.current.firstChild as HTMLElement;
+ firstItem.focus();
+ }
+ }}
>
- `1px solid ${theme.palette.divider}`,
- }}
- onKeyDown={(e) => {
- e.stopPropagation();
- if (e.key === "ArrowDown" && menuListRef.current) {
- const firstItem = menuListRef.current.firstChild as HTMLElement;
- firstItem.focus();
- }
+ borderBottom: `1px solid ${theme.palette.divider}`,
}}
>
theme.palette.text.secondary,
+ color: theme.palette.text.secondary,
}}
/>
- ({
onChange={(e) => {
onQueryChange(e.target.value);
}}
- sx={{
+ css={{
height: "100%",
border: 0,
background: "none",
width: "100%",
- marginLeft: 2,
+ marginLeft: 16,
outline: 0,
"&::placeholder": {
- color: (theme) => theme.palette.text.secondary,
+ color: theme.palette.text.secondary,
},
}}
/>
-
+
-
+
{
@@ -629,22 +660,23 @@ function SearchMenu({
options.length > 0 ? (
options.map(renderOption)
) : (
- theme.palette.text.secondary,
+ color: theme.palette.text.secondary,
textAlign: "center",
- py: 1,
+ paddingTop: 8,
+ paddingBottom: 8,
}}
>
No results
-
+
)
) : (
)}
-
+
);
}
diff --git a/site/src/components/GlobalSnackbar/GlobalSnackbar.tsx b/site/src/components/GlobalSnackbar/GlobalSnackbar.tsx
index 9b97c9be02..a39e9ad880 100644
--- a/site/src/components/GlobalSnackbar/GlobalSnackbar.tsx
+++ b/site/src/components/GlobalSnackbar/GlobalSnackbar.tsx
@@ -45,12 +45,12 @@ export const GlobalSnackbar: FC = () => {
autoHideDuration={notification.msgType === MsgType.Error ? 22000 : 6000}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
message={
-
+
{notification.msgType === MsgType.Error && (
)}
-
+
{notification.msg}
{notification.additionalMsgs &&
@@ -64,30 +64,13 @@ export const GlobalSnackbar: FC = () => {
);
};
-const styles = {
- list: {
- paddingLeft: 0,
- },
- messageWrapper: {
- display: "flex",
- },
- message: {
- maxWidth: 670,
- },
- messageTitle: {
- fontSize: 14,
- fontWeight: 600,
- },
- messageSubtitle: {
- marginTop: 12,
- },
- errorIcon: (theme) => ({
- color: theme.palette.error.contrastText,
- marginRight: 16,
- }),
-} satisfies Record
>;
+interface AdditionalMessageDisplayProps {
+ message: AdditionalMessage;
+}
-function AdditionalMessageDisplay({ message }: { message: AdditionalMessage }) {
+const AdditionalMessageDisplay: FC = ({
+ message,
+}) => {
if (isNotificationText(message)) {
return {message} ;
}
@@ -102,7 +85,7 @@ function AdditionalMessageDisplay({ message }: { message: AdditionalMessage }) {
if (isNotificationList(message)) {
return (
-
+
{message.map((item, idx) => (
{item}
@@ -113,4 +96,18 @@ function AdditionalMessageDisplay({ message }: { message: AdditionalMessage }) {
}
return null;
-}
+};
+
+const styles = {
+ messageTitle: {
+ fontSize: 14,
+ fontWeight: 600,
+ },
+ messageSubtitle: {
+ marginTop: 12,
+ },
+ errorIcon: (theme) => ({
+ color: theme.palette.error.contrastText,
+ marginRight: 16,
+ }),
+} satisfies Record>;
diff --git a/site/src/components/LastSeen/LastSeen.tsx b/site/src/components/LastSeen/LastSeen.tsx
index df9c05210e..d7510c5f76 100644
--- a/site/src/components/LastSeen/LastSeen.tsx
+++ b/site/src/components/LastSeen/LastSeen.tsx
@@ -1,11 +1,12 @@
-import Box, { type BoxProps } from "@mui/material/Box";
import { useTheme } from "@emotion/react";
import dayjs from "dayjs";
+import { type FC, type HTMLAttributes } from "react";
-export const LastSeen = ({
- value,
- ...boxProps
-}: { value: string } & BoxProps) => {
+interface LastSeenProps extends HTMLAttributes {
+ value: string;
+}
+
+export const LastSeen: FC = ({ value, ...attrs }) => {
const theme = useTheme();
const t = dayjs(value);
const now = dayjs();
@@ -29,13 +30,8 @@ export const LastSeen = ({
}
return (
-
+
{message}
-
+
);
};
diff --git a/site/src/components/Loader/Loader.tsx b/site/src/components/Loader/Loader.tsx
index 0a5f487f33..eaa002a660 100644
--- a/site/src/components/Loader/Loader.tsx
+++ b/site/src/components/Loader/Loader.tsx
@@ -1,22 +1,24 @@
-import Box, { BoxProps } from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress";
-import { FC } from "react";
+import { type FC, type HTMLAttributes } from "react";
-export const Loader: FC<{ size?: number } & BoxProps> = ({
- size = 26,
- ...boxProps
-}) => {
+interface LoaderProps extends HTMLAttributes {
+ size?: number;
+}
+
+export const Loader: FC = ({ size = 26, ...attrs }) => {
return (
-
-
+
);
};
diff --git a/site/src/components/OverflowY/OverflowY.tsx b/site/src/components/OverflowY/OverflowY.tsx
index 1252287a94..fe055df828 100644
--- a/site/src/components/OverflowY/OverflowY.tsx
+++ b/site/src/components/OverflowY/OverflowY.tsx
@@ -1,18 +1,21 @@
/**
* @file Provides reusable vertical overflow behavior.
*/
-import { type ReactNode } from "react";
-import { type SystemStyleObject } from "@mui/system";
-import Box from "@mui/system/Box";
+import { type FC, type ReactNode } from "react";
-type Props = {
- children: ReactNode;
+type OverflowYProps = {
+ children?: ReactNode;
+ className?: string;
height?: number;
maxHeight?: number;
- sx?: SystemStyleObject;
};
-export function OverflowY({ children, height, maxHeight, sx }: Props) {
+export const OverflowY: FC
= ({
+ children,
+ height,
+ maxHeight,
+ ...attrs
+}) => {
const computedHeight = height === undefined ? "100%" : `${height}px`;
// Doing Math.max check to catch cases where height is accidentally larger
@@ -23,17 +26,17 @@ export function OverflowY({ children, height, maxHeight, sx }: Props) {
: `${Math.max(height ?? 0, maxHeight)}px`;
return (
-
{children}
-
+
);
-}
+};
diff --git a/site/src/components/Paywall/Paywall.tsx b/site/src/components/Paywall/Paywall.tsx
index 2f9d9ba860..b24acfff07 100644
--- a/site/src/components/Paywall/Paywall.tsx
+++ b/site/src/components/Paywall/Paywall.tsx
@@ -1,21 +1,19 @@
-import Box from "@mui/material/Box";
import { type FC, type ReactNode } from "react";
import { type Interpolation, type Theme } from "@emotion/react";
import { EnterpriseBadge } from "components/Badges/Badges";
import { Stack } from "components/Stack/Stack";
export interface PaywallProps {
+ children?: ReactNode;
message: string;
- description?: string | React.ReactNode;
+ description?: string | ReactNode;
cta?: ReactNode;
}
-export const Paywall: FC
> = (props) => {
- const { message, description, cta } = props;
-
+export const Paywall: FC = ({ message, description, cta }) => {
return (
-
-
+
+
{message}
@@ -24,7 +22,7 @@ export const Paywall: FC> = (props) => {
{description && {description}
}
{cta}
-
+
);
};
@@ -41,9 +39,6 @@ const styles = {
border: `1px solid ${theme.palette.divider}`,
borderRadius: 8,
}),
- header: {
- marginBottom: 24,
- },
title: {
fontWeight: 600,
fontFamily: "inherit",
@@ -58,10 +53,4 @@ const styles = {
color: theme.palette.text.secondary,
fontSize: 14,
}),
- enterpriseChip: (theme) => ({
- background: theme.palette.success.dark,
- color: theme.palette.success.contrastText,
- border: `1px solid ${theme.palette.success.light}`,
- fontSize: 13,
- }),
} satisfies Record
>;
diff --git a/site/src/components/Popover/Popover.tsx b/site/src/components/Popover/Popover.tsx
index bab14ff805..489d3d85dc 100644
--- a/site/src/components/Popover/Popover.tsx
+++ b/site/src/components/Popover/Popover.tsx
@@ -1,6 +1,7 @@
import {
- ReactElement,
- ReactNode,
+ type FC,
+ type ReactElement,
+ type ReactNode,
cloneElement,
createContext,
useContext,
@@ -38,13 +39,19 @@ const PopoverContext = createContext(
undefined,
);
-export const Popover = (props: {
+interface PopoverProps {
children: ReactNode | ((popover: PopoverContextValue) => ReactNode); // Allows inline usage
mode?: TriggerMode;
isDefaultOpen?: boolean;
+}
+
+export const Popover: FC = ({
+ children,
+ mode,
+ isDefaultOpen,
}) => {
const hookId = useId();
- const [isOpen, setIsOpen] = useState(props.isDefaultOpen ?? false);
+ const [isOpen, setIsOpen] = useState(isDefaultOpen ?? false);
const triggerRef = useRef(null);
const value: PopoverContextValue = {
@@ -52,14 +59,12 @@ export const Popover = (props: {
setIsOpen,
triggerRef,
id: `${hookId}-popover`,
- mode: props.mode ?? "click",
+ mode: mode ?? "click",
};
return (
- {typeof props.children === "function"
- ? props.children(value)
- : props.children}
+ {typeof children === "function" ? children(value) : children}
);
};
@@ -102,14 +107,19 @@ export const PopoverTrigger = (props: { children: TriggerElement }) => {
type Horizontal = "left" | "right";
-export const PopoverContent = (
- props: Omit & {
- horizontal?: Horizontal;
- },
-) => {
+type PopoverContentProps = Omit<
+ MuiPopoverProps,
+ "open" | "onClose" | "anchorEl"
+> & {
+ horizontal?: Horizontal;
+};
+
+export const PopoverContent: FC = ({
+ horizontal = "left",
+ ...popoverProps
+}) => {
const popover = usePopover();
const [isReady, setIsReady] = useState(false);
- const horizontal = props.horizontal ?? "left";
const hoverMode = popover.mode === "hover";
// This is a hack to make sure the popover is not rendered until the trigger
@@ -143,7 +153,7 @@ export const PopoverContent = (
}}
{...horizontalProps(horizontal)}
{...modeProps(popover)}
- {...props}
+ {...popoverProps}
id={popover.id}
open={popover.isOpen}
onClose={() => popover.setIsOpen(false)}
diff --git a/site/src/components/ProxyStatusLatency/ProxyStatusLatency.tsx b/site/src/components/ProxyStatusLatency/ProxyStatusLatency.tsx
index 8886652ac2..330aaa542e 100644
--- a/site/src/components/ProxyStatusLatency/ProxyStatusLatency.tsx
+++ b/site/src/components/ProxyStatusLatency/ProxyStatusLatency.tsx
@@ -1,15 +1,19 @@
import { useTheme } from "@mui/material/styles";
import HelpOutline from "@mui/icons-material/HelpOutline";
-import Box from "@mui/material/Box";
import Tooltip from "@mui/material/Tooltip";
-import { FC } from "react";
+import { type FC } from "react";
import { getLatencyColor } from "utils/latency";
import CircularProgress from "@mui/material/CircularProgress";
-export const ProxyStatusLatency: FC<{
+interface ProxyStatusLatencyProps {
latency?: number;
isLoading?: boolean;
-}> = ({ latency, isLoading }) => {
+}
+
+export const ProxyStatusLatency: FC = ({
+ latency,
+ isLoading,
+}) => {
const theme = useTheme();
const color = getLatencyColor(theme, latency);
@@ -18,7 +22,7 @@ export const ProxyStatusLatency: FC<{
+
{latency.toFixed(0)}ms
-
+
);
};
diff --git a/site/src/components/Resources/AgentButton.tsx b/site/src/components/Resources/AgentButton.tsx
index c0ab32c58f..58f0e533b8 100644
--- a/site/src/components/Resources/AgentButton.tsx
+++ b/site/src/components/Resources/AgentButton.tsx
@@ -1,28 +1,35 @@
-import Button, { ButtonProps } from "@mui/material/Button";
+import Button, { type ButtonProps } from "@mui/material/Button";
+import { useTheme } from "@emotion/react";
import { forwardRef } from "react";
// eslint-disable-next-line react/display-name -- Name is inferred from variable name
export const AgentButton = forwardRef(
(props, ref) => {
+ const { children, ...buttonProps } = props;
+ const theme = useTheme();
+
return (
theme.palette.background.default,
+ css={{
+ backgroundColor: theme.palette.background.default,
+
"&:hover": {
- backgroundColor: (theme) => theme.palette.background.paper,
+ backgroundColor: theme.palette.background.paper,
},
+
// Making them smaller since those icons don't have a padding around them
"& .MuiButton-startIcon": {
width: 12,
height: 12,
"& svg": { width: "100%", height: "100%" },
},
- ...props.sx,
}}
- />
+ >
+ {children}
+
);
},
);
diff --git a/site/src/components/Resources/AgentMetadata.tsx b/site/src/components/Resources/AgentMetadata.tsx
index 4145843b03..13ef4dc547 100644
--- a/site/src/components/Resources/AgentMetadata.tsx
+++ b/site/src/components/Resources/AgentMetadata.tsx
@@ -1,29 +1,34 @@
+import dayjs from "dayjs";
+import Skeleton from "@mui/material/Skeleton";
+import Tooltip from "@mui/material/Tooltip";
+import { type Interpolation, type Theme } from "@emotion/react";
+import {
+ createContext,
+ type FC,
+ type HTMLAttributes,
+ useContext,
+ useEffect,
+ useLayoutEffect,
+ useRef,
+ useState,
+} from "react";
import { watchAgentMetadata } from "api/api";
import type {
WorkspaceAgent,
WorkspaceAgentMetadata,
} from "api/typesGenerated";
import { Stack } from "components/Stack/Stack";
-import dayjs from "dayjs";
-import {
- createContext,
- FC,
- useContext,
- useEffect,
- useRef,
- useState,
-} from "react";
-import Skeleton from "@mui/material/Skeleton";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
-import Tooltip from "@mui/material/Tooltip";
-import Box, { BoxProps } from "@mui/material/Box";
-import { type Interpolation, type Theme } from "@emotion/react";
type ItemStatus = "stale" | "valid" | "loading";
export const WatchAgentMetadataContext = createContext(watchAgentMetadata);
-const MetadataItem: FC<{ item: WorkspaceAgentMetadata }> = ({ item }) => {
+interface MetadataItemProps {
+ item: WorkspaceAgentMetadata;
+}
+
+const MetadataItem: FC = ({ item }) => {
if (item.result === undefined) {
throw new Error("Metadata item result is undefined");
}
@@ -79,7 +84,7 @@ const MetadataItem: FC<{ item: WorkspaceAgentMetadata }> = ({ item }) => {
return (
{item.description.display_name}
-
{value}
+
{value}
);
};
@@ -106,10 +111,15 @@ export const AgentMetadataView: FC = ({ metadata }) => {
);
};
-export const AgentMetadata: FC<{
+interface AgentMetadataProps {
agent: WorkspaceAgent;
storybookMetadata?: WorkspaceAgentMetadata[];
-}> = ({ agent, storybookMetadata }) => {
+}
+
+export const AgentMetadata: FC = ({
+ agent,
+ storybookMetadata,
+}) => {
const [metadata, setMetadata] = useState<
WorkspaceAgentMetadata[] | undefined
>(undefined);
@@ -182,10 +192,13 @@ export const AgentMetadataSkeleton: FC = () => {
);
};
-const StaticWidth = (props: BoxProps) => {
+const StaticWidth: FC> = ({
+ children,
+ ...attrs
+}) => {
const ref = useRef(null);
- useEffect(() => {
+ useLayoutEffect(() => {
// Ignore this in storybook
if (!ref.current || process.env.STORYBOOK === "true") {
return;
@@ -196,9 +209,13 @@ const StaticWidth = (props: BoxProps) => {
const autoWidth = ref.current.getBoundingClientRect().width;
ref.current.style.width =
autoWidth > currentWidth ? `${autoWidth}px` : `${currentWidth}px`;
- }, [props.children]);
+ }, [children]);
- return ;
+ return (
+
+ {children}
+
+ );
};
// These are more or less copied from
diff --git a/site/src/components/Resources/PortForwardButton.stories.tsx b/site/src/components/Resources/PortForwardButton.stories.tsx
index 910ab65909..1f4c710e71 100644
--- a/site/src/components/Resources/PortForwardButton.stories.tsx
+++ b/site/src/components/Resources/PortForwardButton.stories.tsx
@@ -1,4 +1,3 @@
-import Box from "@mui/material/Box";
import { PortForwardPopoverView } from "./PortForwardButton";
import type { Meta, StoryObj } from "@storybook/react";
import {
@@ -11,16 +10,16 @@ const meta: Meta = {
component: PortForwardPopoverView,
decorators: [
(Story) => (
- ({
width: 304,
- border: (theme) => `1px solid ${theme.palette.divider}`,
- borderRadius: 1,
- backgroundColor: (theme) => theme.palette.background.paper,
- }}
+ border: `1px solid ${theme.palette.divider}`,
+ borderRadius: 8,
+ backgroundColor: theme.palette.background.paper,
+ })}
>
-
+
),
],
args: {
diff --git a/site/src/components/Resources/PortForwardButton.tsx b/site/src/components/Resources/PortForwardButton.tsx
index 6d1747f32a..c4c2cbf226 100644
--- a/site/src/components/Resources/PortForwardButton.tsx
+++ b/site/src/components/Resources/PortForwardButton.tsx
@@ -155,13 +155,13 @@ export const PortForwardPopoverView: FC = ({
/>
button:hover + button": {
borderLeft: "1px solid #FFF",
@@ -74,9 +74,9 @@ export const VSCodeDesktopButton: FC<
onClick={() => {
setIsVariantMenuOpen(true);
}}
- sx={{ px: 0 }}
+ css={{ paddingLeft: 0, paddingRight: 0 }}
>
-
+
@@ -84,28 +84,28 @@ export const VSCodeDesktopButton: FC<
open={isVariantMenuOpen}
anchorEl={menuAnchorRef.current}
onClose={() => setIsVariantMenuOpen(false)}
- sx={{
+ css={{
"& .MuiMenu-paper": {
width: menuAnchorRef.current?.clientWidth,
},
}}
>
{
selectVariant("vscode");
}}
>
-
+
VS Code Desktop
{
selectVariant("vscode-insiders");
}}
>
-
+
VS Code Insiders
diff --git a/site/src/components/RichParameterInput/RichParameterInput.tsx b/site/src/components/RichParameterInput/RichParameterInput.tsx
index 06367c7852..4ce0d8555d 100644
--- a/site/src/components/RichParameterInput/RichParameterInput.tsx
+++ b/site/src/components/RichParameterInput/RichParameterInput.tsx
@@ -1,4 +1,3 @@
-import Box from "@mui/material/Box";
import FormControlLabel from "@mui/material/FormControlLabel";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
@@ -154,9 +153,9 @@ export const RichParameterInput: FC = ({
data-testid={`parameter-field-${parameter.name}`}
>
-
+
-
+
);
};
@@ -235,7 +234,7 @@ const RichParameterField: React.FC = ({
}
>
- {option.name}
+ {option.name}
) : (
<>
diff --git a/site/src/components/Stats/Stats.tsx b/site/src/components/Stats/Stats.tsx
index 8eca667743..64e18f335b 100644
--- a/site/src/components/Stats/Stats.tsx
+++ b/site/src/components/Stats/Stats.tsx
@@ -1,22 +1,28 @@
import { type CSSObject, type Interpolation, type Theme } from "@emotion/react";
-import Box from "@mui/material/Box";
-import { ReactNode, type ComponentProps, type FC } from "react";
+import { type FC, type HTMLAttributes, type ReactNode } from "react";
-export const Stats: FC> = (props) => {
- return ;
+export const Stats: FC> = ({
+ children,
+ ...attrs
+}) => {
+ return (
+
+ {children}
+
+ );
};
-export const StatsItem: FC<
- {
- label: string;
- value: ReactNode;
- } & ComponentProps
-> = ({ label, value, ...divProps }) => {
+interface StatsItemProps extends HTMLAttributes {
+ label: string;
+ value: ReactNode;
+}
+
+export const StatsItem: FC = ({ label, value, ...attrs }) => {
return (
-
+
{label}:
{value}
-
+
);
};
diff --git a/site/src/components/TableEmpty/TableEmpty.tsx b/site/src/components/TableEmpty/TableEmpty.tsx
index d08a01f969..be7d0917d0 100644
--- a/site/src/components/TableEmpty/TableEmpty.tsx
+++ b/site/src/components/TableEmpty/TableEmpty.tsx
@@ -1,14 +1,17 @@
import TableCell from "@mui/material/TableCell";
import TableRow from "@mui/material/TableRow";
-import { FC } from "react";
-import { EmptyState, EmptyStateProps } from "components/EmptyState/EmptyState";
+import { type FC } from "react";
+import {
+ EmptyState,
+ type EmptyStateProps,
+} from "components/EmptyState/EmptyState";
export type TableEmptyProps = EmptyStateProps;
export const TableEmpty: FC = (props) => {
return (
-
+
diff --git a/site/src/components/TableLoader/TableLoader.tsx b/site/src/components/TableLoader/TableLoader.tsx
index c802452ba3..7c227c870d 100644
--- a/site/src/components/TableLoader/TableLoader.tsx
+++ b/site/src/components/TableLoader/TableLoader.tsx
@@ -13,12 +13,14 @@ export const TableLoader: FC = () => {
);
};
-export const TableLoaderSkeleton = ({
+interface TableLoaderSkeletonProps {
+ rows?: number;
+ children?: ReactNode;
+}
+
+export const TableLoaderSkeleton: FC = ({
rows = 4,
children,
-}: {
- rows?: number;
- children: ReactNode;
}) => {
if (!isValidElement(children)) {
throw new Error(
@@ -34,6 +36,13 @@ export const TableLoaderSkeleton = ({
);
};
-export const TableRowSkeleton = (props: TableRowProps) => {
- return ;
+export const TableRowSkeleton: FC = ({
+ children,
+ ...rowProps
+}) => {
+ return (
+
+ {children}
+
+ );
};
diff --git a/site/src/components/Tabs/Tabs.tsx b/site/src/components/Tabs/Tabs.tsx
index f98820e6ec..bc1f7d7406 100644
--- a/site/src/components/Tabs/Tabs.tsx
+++ b/site/src/components/Tabs/Tabs.tsx
@@ -29,7 +29,11 @@ interface TabLinkProps extends NavLinkProps {
className?: string;
}
-export const TabLink: FC = ({ className, ...linkProps }) => {
+export const TabLink: FC = ({
+ className,
+ children,
+ ...linkProps
+}) => {
const tabLink = useClassName(classNames.tabLink, []);
const activeTabLink = useClassName(classNames.activeTabLink, []);
@@ -39,7 +43,9 @@ export const TabLink: FC = ({ className, ...linkProps }) => {
cx([tabLink, isActive && activeTabLink, className])
}
{...linkProps}
- />
+ >
+ {children}
+
);
};
diff --git a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx
index c41ad89417..30392cf242 100644
--- a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx
+++ b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx
@@ -1,10 +1,9 @@
import dayjs from "dayjs";
-import { type ComponentProps, type FC, Fragment } from "react";
+import { type FC, Fragment, type HTMLAttributes } from "react";
import type { ProvisionerJobLog } from "api/typesGenerated";
import { BODY_FONT_FAMILY, MONOSPACE_FONT_FAMILY } from "theme/constants";
import { Logs } from "./Logs";
-import Box from "@mui/material/Box";
-import { type Interpolation, type Theme } from "@emotion/react";
+import { type Interpolation, type Theme, useTheme } from "@emotion/react";
const Language = {
seconds: "seconds",
@@ -38,30 +37,30 @@ const getStageDurationInSeconds = (logs: ProvisionerJobLog[]) => {
return completedAt.diff(startedAt, "seconds");
};
-export type WorkspaceBuildLogsProps = {
- logs: ProvisionerJobLog[];
- sticky?: boolean;
+interface WorkspaceBuildLogsProps extends HTMLAttributes {
hideTimestamps?: boolean;
-} & ComponentProps;
+ sticky?: boolean;
+ logs: ProvisionerJobLog[];
+}
export const WorkspaceBuildLogs: FC = ({
hideTimestamps,
sticky,
logs,
- ...boxProps
+ ...attrs
}) => {
+ const theme = useTheme();
const groupedLogsByStage = groupLogsByStage(logs);
const stages = Object.keys(groupedLogsByStage);
return (
- `1px solid ${theme.palette.divider}`,
- borderRadius: 1,
+
{stages.map((stage) => {
const logs = groupedLogsByStage[stage];
@@ -92,7 +91,7 @@ export const WorkspaceBuildLogs: FC = ({
);
})}
-
+
);
};
diff --git a/site/src/components/WorkspaceDeletion/DormantWorkspaceBanner.tsx b/site/src/components/WorkspaceDeletion/DormantWorkspaceBanner.tsx
index 66bfc989ae..2022751555 100644
--- a/site/src/components/WorkspaceDeletion/DormantWorkspaceBanner.tsx
+++ b/site/src/components/WorkspaceDeletion/DormantWorkspaceBanner.tsx
@@ -1,26 +1,29 @@
-import { Workspace } from "api/typesGenerated";
-import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider";
-import { Alert } from "components/Alert/Alert";
import { formatDistanceToNow } from "date-fns";
import Link from "@mui/material/Link";
+import { type FC } from "react";
import { Link as RouterLink } from "react-router-dom";
+import type { Workspace } from "api/typesGenerated";
+import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider";
+import { Alert } from "components/Alert/Alert";
export enum Count {
Singular,
Multiple,
}
-export const DormantWorkspaceBanner = ({
- workspaces,
- onDismiss,
- shouldRedisplayBanner,
- count = Count.Singular,
-}: {
+interface DormantWorkspaceBannerProps {
workspaces?: Workspace[];
onDismiss: () => void;
shouldRedisplayBanner: boolean;
count?: Count;
-}): JSX.Element | null => {
+}
+
+export const DormantWorkspaceBanner: FC = ({
+ workspaces,
+ onDismiss,
+ shouldRedisplayBanner,
+ count = Count.Singular,
+}) => {
const experimentEnabled = useIsWorkspaceActionsEnabled();
if (!workspaces) {
diff --git a/site/src/pages/AuditPage/AuditFilter.tsx b/site/src/pages/AuditPage/AuditFilter.tsx
index e74f01aff3..e0e73ff997 100644
--- a/site/src/pages/AuditPage/AuditFilter.tsx
+++ b/site/src/pages/AuditPage/AuditFilter.tsx
@@ -1,3 +1,5 @@
+import capitalize from "lodash/capitalize";
+import { type FC } from "react";
import { AuditActions, ResourceTypes } from "api/typesGenerated";
import { UserFilterMenu, UserMenu } from "components/Filter/UserFilter";
import {
@@ -10,7 +12,6 @@ import {
} from "components/Filter/filter";
import { UseFilterMenuOptions, useFilterMenu } from "components/Filter/menu";
import { BaseOption } from "components/Filter/options";
-import capitalize from "lodash/capitalize";
import { docs } from "utils/docs";
const PRESET_FILTERS = [
@@ -30,11 +31,7 @@ const PRESET_FILTERS = [
},
];
-export const AuditFilter = ({
- filter,
- error,
- menus,
-}: {
+interface AuditFilterProps {
filter: ReturnType;
error?: unknown;
menus: {
@@ -42,7 +39,9 @@ export const AuditFilter = ({
action: ActionFilterMenu;
resourceType: ResourceTypeFilterMenu;
};
-}) => {
+}
+
+export const AuditFilter: FC = ({ filter, error, menus }) => {
return (
-
+
>
}
skeleton={
diff --git a/site/src/pages/CreateUserPage/CreateUserForm.tsx b/site/src/pages/CreateUserPage/CreateUserForm.tsx
index 630316aea8..c5afa5ffe0 100644
--- a/site/src/pages/CreateUserPage/CreateUserForm.tsx
+++ b/site/src/pages/CreateUserPage/CreateUserForm.tsx
@@ -112,7 +112,7 @@ export const CreateUserForm: FC<
return (
{isApiError(error) && !hasApiFieldErrors(error) && (
-
+
)}
- {/* 62px - navbar and 36px - the bottom bar */}
-
+
-
-
+
+
);
-}
+};
+
+const styles = {
+ sectionLink: (theme) => ({
+ border: "none",
+ fontSize: 14,
+ width: "100%",
+ display: "flex",
+ alignItems: "center",
+ gap: 12,
+ textAlign: "left",
+ height: 36,
+ padding: "0 24px",
+ cursor: "pointer",
+ background: "none",
+ color: theme.palette.text.secondary,
+
+ "&:hover": {
+ background: theme.palette.action.hover,
+ color: theme.palette.text.primary,
+ },
+ }),
+
+ activeSectionLink: (theme) => ({
+ background: theme.palette.action.hover,
+ pointerEvents: "none",
+ color: theme.palette.text.primary,
+ }),
+} satisfies Record>;
diff --git a/site/src/pages/IconsPage/IconsPage.tsx b/site/src/pages/IconsPage/IconsPage.tsx
index 06199bdf9b..4122a828ba 100644
--- a/site/src/pages/IconsPage/IconsPage.tsx
+++ b/site/src/pages/IconsPage/IconsPage.tsx
@@ -2,7 +2,6 @@ import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import Tooltip from "@mui/material/Tooltip";
import IconButton from "@mui/material/IconButton";
-import Box from "@mui/material/Box";
import Link from "@mui/material/Link";
import SearchIcon from "@mui/icons-material/SearchOutlined";
import ClearIcon from "@mui/icons-material/CloseOutlined";
@@ -80,7 +79,7 @@ export const IconsPage: FC = () => {
{
public GitHub repository. Just keep in mind that it should be
relevant to many Coder users, and redistributable under a
permissive license.
-
+
}
>
@@ -126,7 +125,7 @@ export const IconsPage: FC = () => {
startAdornment: (
{
size="small"
onClick={() => setSearchInputText("")}
>
-
+
diff --git a/site/src/pages/LoginPage/OAuthSignInForm.tsx b/site/src/pages/LoginPage/OAuthSignInForm.tsx
index e8f289f122..299040b768 100644
--- a/site/src/pages/LoginPage/OAuthSignInForm.tsx
+++ b/site/src/pages/LoginPage/OAuthSignInForm.tsx
@@ -1,30 +1,29 @@
import Button from "@mui/material/Button";
import GitHubIcon from "@mui/icons-material/GitHub";
import KeyIcon from "@mui/icons-material/VpnKey";
-import Box from "@mui/material/Box";
-import { useId, type FC } from "react";
+import { type FC, useId } from "react";
import { Language } from "./SignInForm";
-import { type AuthMethods } from "api/typesGenerated";
+import type { AuthMethods } from "api/typesGenerated";
import { visuallyHidden } from "@mui/utils";
+const iconStyles = {
+ width: 16,
+ height: 16,
+};
+
type OAuthSignInFormProps = {
isSigningIn: boolean;
redirectTo: string;
authMethods?: AuthMethods;
};
-const iconStyles = {
- width: 16,
- height: 16,
-};
-
export const OAuthSignInForm: FC = ({
isSigningIn,
redirectTo,
authMethods,
}) => {
return (
-
+
{authMethods?.github.enabled && (
= ({
{authMethods.oidc.signInText || Language.oidcSignIn}
)}
-
+
);
};
@@ -72,7 +71,7 @@ type OidcIconProps = {
iconUrl: string;
};
-function OidcIcon({ iconUrl }: OidcIconProps) {
+const OidcIcon: FC = ({ iconUrl }) => {
const hookId = useId();
const oidcId = `${hookId}-oidc`;
@@ -87,4 +86,4 @@ function OidcIcon({ iconUrl }: OidcIconProps) {
>
);
-}
+};
diff --git a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx
index f040033f2a..14593a3dc8 100644
--- a/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx
+++ b/site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx
@@ -1,6 +1,5 @@
import CheckOutlined from "@mui/icons-material/CheckOutlined";
import FileCopyOutlined from "@mui/icons-material/FileCopyOutlined";
-import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import FormControlLabel from "@mui/material/FormControlLabel";
import Radio from "@mui/material/Radio";
@@ -101,8 +100,8 @@ export const TemplateEmbedPageView: FC<{
{!buttonValues || !templateParameters ? (
) : (
-
-
+
+
)}
-
-
+
+ ({
// 80px for padding, 36px is for the status bar. We want to use `vh`
// so that it will be relative to the screen and not the parent layout.
height: "calc(100vh - (80px + 36px))",
top: 40,
position: "sticky",
- }}
- p={8}
- flex={1}
- alignItems="center"
- justifyContent="center"
- borderRadius={1}
- bgcolor="background.paper"
- border={(theme) => `1px solid ${theme.palette.divider}`}
+ display: "flex",
+ padding: 64,
+ flex: 1,
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: 8,
+ backgroundColor: theme.palette.background.paper,
+ border: `1px solid ${theme.palette.divider}`,
+ })}
>
-
:
}
@@ -183,9 +184,9 @@ export const TemplateEmbedPageView: FC<{
>
Copy button code
-
-
-
+
+
+
)}
>
);
diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/IntervalMenu.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/IntervalMenu.tsx
index 797e1b4995..29127893d9 100644
--- a/site/src/pages/TemplatePage/TemplateInsightsPage/IntervalMenu.tsx
+++ b/site/src/pages/TemplatePage/TemplateInsightsPage/IntervalMenu.tsx
@@ -1,10 +1,9 @@
import CheckOutlined from "@mui/icons-material/CheckOutlined";
import ExpandMoreOutlined from "@mui/icons-material/ExpandMoreOutlined";
-import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
-import { useState, useRef } from "react";
+import { type FC, useState, useRef } from "react";
export const insightsIntervals = {
day: {
@@ -17,13 +16,12 @@ export const insightsIntervals = {
export type InsightsInterval = keyof typeof insightsIntervals;
-export const IntervalMenu = ({
- value,
- onChange,
-}: {
+interface IntervalMenuProps {
value: InsightsInterval;
onChange: (value: InsightsInterval) => void;
-}) => {
+}
+
+export const IntervalMenu: FC = ({ value, onChange }) => {
const anchorRef = useRef(null);
const [open, setOpen] = useState(false);
@@ -72,11 +70,11 @@ export const IntervalMenu = ({
}}
>
{label}
-
+
{value === interval && (
)}
-
+
);
})}
diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/WeekPicker.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/WeekPicker.tsx
index 73ad35db9d..6b4525566e 100644
--- a/site/src/pages/TemplatePage/TemplateInsightsPage/WeekPicker.tsx
+++ b/site/src/pages/TemplatePage/TemplateInsightsPage/WeekPicker.tsx
@@ -1,10 +1,9 @@
import CheckOutlined from "@mui/icons-material/CheckOutlined";
import ExpandMoreOutlined from "@mui/icons-material/ExpandMoreOutlined";
-import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
-import { useState, useRef } from "react";
+import { type FC, useState, useRef } from "react";
import { DateRangeValue } from "./DateRange";
import { differenceInWeeks } from "date-fns";
import { lastWeeks } from "./utils";
@@ -13,13 +12,12 @@ import { lastWeeks } from "./utils";
// 6 months.
export const numberOfWeeksOptions = [4, 12, 24] as const;
-export const WeekPicker = ({
- value,
- onChange,
-}: {
+interface WeekPickerProps {
value: DateRangeValue;
onChange: (value: DateRangeValue) => void;
-}) => {
+}
+
+export const WeekPicker: FC = ({ value, onChange }) => {
const anchorRef = useRef(null);
const [open, setOpen] = useState(false);
const numberOfWeeks = differenceInWeeks(value.endDate, value.startDate);
@@ -71,11 +69,11 @@ export const WeekPicker = ({
}}
>
Last {option} weeks
-
+
{numberOfWeeks === option && (
)}
-
+
);
})}
diff --git a/site/src/pages/TemplatePage/TemplateVersionsPage/VersionsTable.tsx b/site/src/pages/TemplatePage/TemplateVersionsPage/VersionsTable.tsx
index cbeb3e208a..91a724a5d8 100644
--- a/site/src/pages/TemplatePage/TemplateVersionsPage/VersionsTable.tsx
+++ b/site/src/pages/TemplatePage/TemplateVersionsPage/VersionsTable.tsx
@@ -1,4 +1,3 @@
-import Box from "@mui/material/Box";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
@@ -6,7 +5,7 @@ import TableContainer from "@mui/material/TableContainer";
import TableRow from "@mui/material/TableRow";
import { Timeline } from "components/Timeline/Timeline";
import { type FC } from "react";
-import * as TypesGen from "api/typesGenerated";
+import type * as TypesGen from "api/typesGenerated";
import { EmptyState } from "components/EmptyState/EmptyState";
import { TableLoader } from "components/TableLoader/TableLoader";
import { VersionRow } from "./VersionRow";
@@ -20,15 +19,17 @@ export const Language = {
export interface VersionsTableProps {
activeVersionId: string;
+ versions?: TypesGen.TemplateVersion[];
onPromoteClick?: (templateVersionId: string) => void;
onArchiveClick?: (templateVersionId: string) => void;
-
- versions?: TypesGen.TemplateVersion[];
}
-export const VersionsTable: FC = (props) => {
- const { versions, onArchiveClick, onPromoteClick, activeVersionId } = props;
-
+export const VersionsTable: FC = ({
+ activeVersionId,
+ versions,
+ onArchiveClick,
+ onPromoteClick,
+}) => {
const latestVersionId = versions?.reduce(
(latestSoFar, against) => {
if (against.job.status !== "succeeded") {
@@ -73,9 +74,9 @@ export const VersionsTable: FC = (props) => {
{versions && versions.length === 0 && (
-
+
-
+
)}
diff --git a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx
index 70a3001d73..dac80188af 100644
--- a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx
+++ b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/UserOrGroupAutocomplete.tsx
@@ -1,8 +1,7 @@
import CircularProgress from "@mui/material/CircularProgress";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
-import Box from "@mui/material/Box";
-import { type ChangeEvent, useState } from "react";
+import { type ChangeEvent, type FC, useState } from "react";
import { css } from "@emotion/react";
import type { Group, User } from "api/typesGenerated";
import { AvatarData } from "components/AvatarData/AvatarData";
@@ -21,13 +20,13 @@ export type UserOrGroupAutocompleteProps = {
exclude: UserOrGroupAutocompleteValue[];
};
-export const UserOrGroupAutocomplete: React.FC<
- UserOrGroupAutocompleteProps
-> = ({ value, onChange, templateID, exclude }) => {
- const [autoComplete, setAutoComplete] = useState<{
- value: string;
- open: boolean;
- }>({
+export const UserOrGroupAutocomplete: FC = ({
+ value,
+ onChange,
+ templateID,
+ exclude,
+}) => {
+ const [autoComplete, setAutoComplete] = useState({
value: "",
open: false,
});
@@ -90,7 +89,7 @@ export const UserOrGroupAutocomplete: React.FC<
const isOptionGroup = isGroup(option);
return (
-
+
-
+
);
}}
options={options}
diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/AutostopRequirementHelperText.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/AutostopRequirementHelperText.tsx
index fff5cf23c5..2ae948bcfc 100644
--- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/AutostopRequirementHelperText.tsx
+++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/AutostopRequirementHelperText.tsx
@@ -1,5 +1,6 @@
-import { Template } from "api/typesGenerated";
-import { TemplateAutostopRequirementDaysValue } from "utils/schedule";
+import { type FC } from "react";
+import type { Template } from "api/typesGenerated";
+import type { TemplateAutostopRequirementDaysValue } from "utils/schedule";
const autostopRequirementDescriptions = {
off: "Workspaces are not required to stop periodically.",
@@ -26,21 +27,24 @@ export const convertAutostopRequirementDaysValue = (
return "off";
};
-export const AutostopRequirementDaysHelperText = ({
- days = "off",
-}: {
+interface AutostopRequirementDaysHelperTextProps {
days: TemplateAutostopRequirementDaysValue;
-}) => {
+}
+
+export const AutostopRequirementDaysHelperText: FC<
+ AutostopRequirementDaysHelperTextProps
+> = ({ days = "off" }) => {
return {autostopRequirementDescriptions[days]} ;
};
-export const AutostopRequirementWeeksHelperText = ({
- days,
- weeks,
-}: {
+interface AutostopRequirementWeeksHelperTextProps {
days: TemplateAutostopRequirementDaysValue;
weeks: number;
-}) => {
+}
+
+export const AutostopRequirementWeeksHelperText: FC<
+ AutostopRequirementWeeksHelperTextProps
+> = ({ days, weeks }) => {
// Disabled
if (days !== "saturday" && days !== "sunday") {
return (
diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/ScheduleDialog.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/ScheduleDialog.tsx
index 2203e1a165..4eeb687a0d 100644
--- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/ScheduleDialog.tsx
+++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/ScheduleDialog.tsx
@@ -71,9 +71,7 @@ export const ScheduleDialog: FC> = ({
{`
This change will result in ${inactiveWorkspacesToGoDormant} workspaces being immediately transitioned to the dormant state and ${inactiveWorkspacesToGoDormantInWeek} over the next seven days. To prevent this, do you want to reset the inactivity period for all template workspaces?`}
> = ({
{showDeletionWarning && (
<>
- {"Dormancy Auto-Deletion"}
+ Dormancy Auto-Deletion
- {`This change will result in ${dormantWorkspacesToBeDeleted} workspaces being immediately deleted and ${dormantWorkspacesToBeDeletedInWeek} over the next 7 days. To prevent this, do you want to reset the dormancy period for all template workspaces?`}
+
+ This change will result in {dormantWorkspacesToBeDeleted}{" "}
+ workspaces being immediately deleted and{" "}
+ {dormantWorkspacesToBeDeletedInWeek} over the next 7 days. To
+ prevent this, do you want to reset the dormancy period for all
+ template workspaces?
+
= ({
- `1px solid ${theme.palette.divider}`,
- borderLeft: (theme) =>
- `2px solid ${theme.palette.error.main}`,
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ borderLeft: `2px solid ${theme.palette.error.main}`,
}}
>
Error during the build
@@ -645,17 +644,19 @@ export const TemplateVersionEditor: FC = ({
);
};
-const TopbarButton = (props: ButtonProps) => {
+const TopbarButton: FC = ({ children, ...buttonProps }) => {
return (
+ >
+ {children}
+
);
};
diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx
index 085def04a8..6a9d4849c3 100644
--- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx
+++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx
@@ -42,7 +42,6 @@ import { Avatar } from "components/Avatar/Avatar";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { docs } from "utils/docs";
import Skeleton from "@mui/material/Skeleton";
-import { Box } from "@mui/system";
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
import { Pill } from "components/Pill/Pill";
@@ -62,7 +61,7 @@ export const Language = {
templateTooltipLink: "Manage templates",
};
-const TemplateHelpTooltip: React.FC = () => {
+const TemplateHelpTooltip: FC = () => {
return (
{Language.templateTooltipTitle}
@@ -76,7 +75,11 @@ const TemplateHelpTooltip: React.FC = () => {
);
};
-const TemplateRow: FC<{ template: Template }> = ({ template }) => {
+interface TemplateRowProps {
+ template: Template;
+}
+
+const TemplateRow: FC = ({ template }) => {
const templatePageLink = `/templates/${template.name}`;
const hasIcon = template.icon && template.icon !== "";
const navigate = useNavigate();
@@ -226,14 +229,14 @@ export const TemplatesPageView: FC = ({
);
};
-const TableLoader = () => {
+const TableLoader: FC = () => {
return (
-
+
diff --git a/site/src/pages/TerminalPage/TerminalAlerts.tsx b/site/src/pages/TerminalPage/TerminalAlerts.tsx
index 05706a5bc4..ba4f8a8470 100644
--- a/site/src/pages/TerminalPage/TerminalAlerts.tsx
+++ b/site/src/pages/TerminalPage/TerminalAlerts.tsx
@@ -1,10 +1,10 @@
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import { Alert, AlertProps } from "components/Alert/Alert";
-import { useState } from "react";
+import { type FC, useState } from "react";
import { docs } from "utils/docs";
-export const ErrorScriptAlert = () => {
+export const ErrorScriptAlert: FC = () => {
return (
{
);
};
-export const LoadingScriptsAlert = () => {
+export const LoadingScriptsAlert: FC = () => {
return (
{
);
};
-export const LoadedScriptsAlert = () => {
+export const LoadedScriptsAlert: FC = () => {
return (
{
);
};
-const TerminalAlert = (props: AlertProps) => {
+const TerminalAlert: FC = (props) => {
return (
({
borderRadius: 0,
borderWidth: 0,
borderBottomWidth: 1,
- borderBottomColor: (theme) => theme.palette.divider,
- backgroundColor: (theme) => theme.palette.background.paper,
- borderLeft: (theme) =>
- `3px solid ${theme.palette[props.severity!].light}`,
+ borderBottomColor: theme.palette.divider,
+ backgroundColor: theme.palette.background.paper,
+ borderLeft: `3px solid ${theme.palette[props.severity!].light}`,
marginBottom: 1,
- }}
+ })}
/>
);
};
-export const DisconnectedAlert = (props: AlertProps) => {
+export const DisconnectedAlert: FC = (props) => {
return (
{
);
};
-const RefreshSessionButton = () => {
+const RefreshSessionButton: FC = () => {
const [isRefreshing, setIsRefreshing] = useState(false);
return (
diff --git a/site/src/pages/TerminalPage/TerminalPage.tsx b/site/src/pages/TerminalPage/TerminalPage.tsx
index c27286864e..156e41daea 100644
--- a/site/src/pages/TerminalPage/TerminalPage.tsx
+++ b/site/src/pages/TerminalPage/TerminalPage.tsx
@@ -14,7 +14,6 @@ import "xterm/css/xterm.css";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
import { pageTitle } from "utils/page";
import { useProxy } from "contexts/ProxyContext";
-import Box from "@mui/material/Box";
import { useDashboard } from "components/Dashboard/DashboardProvider";
import type { Region } from "api/typesGenerated";
import { getLatencyColor } from "utils/latency";
@@ -309,7 +308,7 @@ const TerminalPage: FC = () => {
: ""}
-
+
{lifecycleState === "start_error" && }
{lifecycleState === "starting" && }
{lifecycleState === "ready" &&
@@ -321,31 +320,35 @@ const TerminalPage: FC = () => {
latency && (
)}
-
+
>
);
};
-const BottomBar = ({ proxy, latency }: { proxy: Region; latency?: number }) => {
+interface BottomBarProps {
+ proxy: Region;
+ latency?: number;
+}
+
+const BottomBar: FC = ({ proxy, latency }) => {
const theme = useTheme();
const color = getLatencyColor(theme, latency);
return (
- theme.palette.background.paper,
+ background: theme.palette.background.paper,
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
fontSize: 12,
- borderTop: (theme) => `1px solid ${theme.palette.divider}`,
+ borderTop: `1px solid ${theme.palette.divider}`,
}}
>
- {
padding: 8,
}}
>
-
-
+
{
horizontal: "right",
}}
>
- theme.palette.text.secondary,
+ color: theme.palette.text.secondary,
fontWeight: 500,
}}
>
Selected proxy
-
-
+
-
-
-
+
+
-
+
{proxy.display_name}
-
+
-
+
-
+
);
};
diff --git a/site/src/pages/TerminalPage/TerminalPageAlert.tsx b/site/src/pages/TerminalPage/TerminalPageAlert.tsx
deleted file mode 100644
index ab7f69d21f..0000000000
--- a/site/src/pages/TerminalPage/TerminalPageAlert.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { AlertColor } from "@mui/material/Alert/Alert";
-import Button from "@mui/material/Button";
-import Link from "@mui/material/Link";
-import { Alert } from "components/Alert/Alert";
-import { ReactNode } from "react";
-import { docs } from "utils/docs";
-
-export type TerminalPageAlertType = "error" | "starting" | "success";
-
-type MapAlertTypeToComponent = {
- [key in TerminalPageAlertType]: {
- severity: AlertColor;
- children: ReactNode | undefined;
- };
-};
-
-const mapAlertTypeToText: MapAlertTypeToComponent = {
- error: {
- severity: "warning",
- children: (
- <>
- The workspace{" "}
-
- startup script has exited with an error
-
- , we recommend reloading this session and{" "}
-
- debugging the startup script
- {" "}
- because{" "}
-
- your workspace may be incomplete.
- {" "}
- >
- ),
- },
- starting: {
- severity: "info",
- children: (
- <>
- Startup script is still running. You can continue using this terminal,
- but{" "}
-
- {" "}
- your workspace may be incomplete.
-
- >
- ),
- },
- success: {
- severity: "success",
- children: (
- <>
- Startup script has completed successfully. The workspace is ready but
- this{" "}
-
- session was started before the startup script finished.
- {" "}
- To ensure your shell environment is up-to-date, we recommend reloading
- this session.
- >
- ),
- },
-};
-
-export default ({
- alertType,
- onDismiss,
-}: {
- alertType: TerminalPageAlertType;
- onDismiss: () => void;
-}) => {
- const severity = mapAlertTypeToText[alertType].severity;
- return (
- theme.palette.divider,
- backgroundColor: (theme) => theme.palette.background.paper,
- borderLeft: (theme) => `3px solid ${theme.palette[severity].light}`,
- marginBottom: 1,
- }}
- onDismiss={onDismiss}
- dismissible
- actions={[
- {
- // By redirecting the user without the session in the URL we
- // create a new one
- window.location.href = window.location.pathname;
- }}
- >
- Refresh session
- ,
- ]}
- >
- {mapAlertTypeToText[alertType].children}
-
- );
-};
diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx
index e7b46df07d..a9d785917f 100644
--- a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx
+++ b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx
@@ -1,16 +1,15 @@
+import { Stack } from "@mui/system";
import { type FC } from "react";
+import { useQuery } from "react-query";
+import { useOrganizationId } from "hooks";
import { useMe } from "hooks/useMe";
import { usePermissions } from "hooks/usePermissions";
-import { useQuery } from "react-query";
import { groupsForUser } from "api/queries/groups";
-import { useOrganizationId } from "hooks";
import { useAuth } from "components/AuthProvider/AuthProvider";
+import { Section } from "components/SettingsLayout/Section";
import { useDashboard } from "components/Dashboard/DashboardProvider";
-
-import { Stack } from "@mui/system";
import { AccountUserGroups } from "./AccountUserGroups";
import { AccountForm } from "./AccountForm";
-import { Section } from "components/SettingsLayout/Section";
export const AccountPage: FC = () => {
const me = useMe();
@@ -38,7 +37,6 @@ export const AccountPage: FC = () => {
/>
- {/* Has embedded inside because its description is dynamic */}
{hasGroupsFeature && (
= ({
groups,
error,
loading,
-}: AccountGroupsProps) {
+}) => {
const theme = useTheme();
return (
@@ -71,4 +71,4 @@ export function AccountUserGroups({
);
-}
+};
diff --git a/site/src/pages/UserSettingsPage/SSHKeysPage/SSHKeysPageView.tsx b/site/src/pages/UserSettingsPage/SSHKeysPage/SSHKeysPageView.tsx
index a371d487a4..c3ac6a1c47 100644
--- a/site/src/pages/UserSettingsPage/SSHKeysPage/SSHKeysPageView.tsx
+++ b/site/src/pages/UserSettingsPage/SSHKeysPage/SSHKeysPageView.tsx
@@ -1,4 +1,3 @@
-import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import CircularProgress from "@mui/material/CircularProgress";
import { type FC, type PropsWithChildren } from "react";
@@ -25,9 +24,9 @@ export const SSHKeysPageView: FC> = ({
if (isLoading) {
return (
-
+
-
+
);
}
diff --git a/site/src/pages/UserSettingsPage/SecurityPage/SecurityPage.tsx b/site/src/pages/UserSettingsPage/SecurityPage/SecurityPage.tsx
index 5d06b74f13..124b7350f8 100644
--- a/site/src/pages/UserSettingsPage/SecurityPage/SecurityPage.tsx
+++ b/site/src/pages/UserSettingsPage/SecurityPage/SecurityPage.tsx
@@ -57,16 +57,18 @@ export const SecurityPage: FC = () => {
);
};
-export const SecurityPageView = ({
- security,
- oidc,
-}: {
+interface SecurityPageViewProps {
security: {
form: ComponentProps;
};
oidc?: {
section: ComponentProps;
};
+}
+
+export const SecurityPageView: FC = ({
+ security,
+ oidc,
}) => {
return (
diff --git a/site/src/pages/UserSettingsPage/SecurityPage/SingleSignOnSection.tsx b/site/src/pages/UserSettingsPage/SecurityPage/SingleSignOnSection.tsx
index 3aff1d7881..87f8f61ba3 100644
--- a/site/src/pages/UserSettingsPage/SecurityPage/SingleSignOnSection.tsx
+++ b/site/src/pages/UserSettingsPage/SecurityPage/SingleSignOnSection.tsx
@@ -1,7 +1,7 @@
-import { useState } from "react";
+import { useTheme } from "@emotion/react";
+import { type FC, useState } from "react";
import { Section } from "components/SettingsLayout/Section";
import TextField from "@mui/material/TextField";
-import Box from "@mui/material/Box";
import GitHubIcon from "@mui/icons-material/GitHub";
import KeyIcon from "@mui/icons-material/VpnKey";
import Button from "@mui/material/Button";
@@ -99,7 +99,7 @@ export const useSingleSignOnSection = () => {
};
};
-function SSOEmptyState() {
+const SSOEmptyState: FC = () => {
return (
({
@@ -117,14 +117,14 @@ function SSOEmptyState() {
}
/>
);
-}
+};
type SingleSignOnSectionProps = ReturnType & {
authMethods: AuthMethods;
userLoginType: UserLoginType;
};
-export const SingleSignOnSection = ({
+export const SingleSignOnSection: FC = ({
authMethods,
userLoginType,
openConfirmation,
@@ -133,11 +133,12 @@ export const SingleSignOnSection = ({
isUpdating,
isConfirming,
error,
-}: SingleSignOnSectionProps) => {
+}) => {
+ const theme = useTheme();
+
const authList = Object.values(
authMethods,
) as (typeof authMethods)[keyof typeof authMethods][];
-
const noSsoEnabled = !authList.some((method) => method.enabled);
return (
@@ -147,7 +148,7 @@ export const SingleSignOnSection = ({
title="Single Sign On"
description="Authenticate in Coder using one-click"
>
-
+
{userLoginType.login_type === "password" ? (
<>
{authMethods.github.enabled && (
@@ -155,7 +156,7 @@ export const SingleSignOnSection = ({
size="large"
fullWidth
disabled={isUpdating}
- startIcon={
}
+ startIcon={
}
onClick={() => openConfirmation("github")}
>
GitHub
@@ -177,21 +178,21 @@ export const SingleSignOnSection = ({
{noSsoEnabled &&
}
>
) : (
-
theme.palette.background.paper,
- borderRadius: 1,
- border: (theme) => `1px solid ${theme.palette.divider}`,
- padding: 2,
+
theme.palette.success.light,
+ css={{
+ color: theme.palette.success.light,
fontSize: 16,
}}
/>
@@ -203,16 +204,16 @@ export const SingleSignOnSection = ({
: getOIDCLabel(authMethods.oidc)}
-
+
{userLoginType.login_type === "github" ? (
-
+
) : (
)}
-
-
+
+
)}
-
+
{
+interface OIDCIconProps {
+ oidcAuth: OIDCAuthMethod;
+}
+
+const OIDCIcon: FC = ({ oidcAuth }) => {
if (!oidcAuth.iconUrl) {
- return ;
+ return ;
}
return (
-
);
};
@@ -245,18 +249,20 @@ const getOIDCLabel = (oidcAuth: OIDCAuthMethod) => {
return oidcAuth.signInText || "OpenID Connect";
};
-const ConfirmLoginTypeChangeModal = ({
- open,
- loading,
- error,
- onClose,
- onConfirm,
-}: {
+interface ConfirmLoginTypeChangeModalProps {
open: boolean;
loading: boolean;
error: unknown;
onClose: () => void;
onConfirm: (password: string) => void;
+}
+
+const ConfirmLoginTypeChangeModal: FC = ({
+ open,
+ loading,
+ error,
+ onClose,
+ onConfirm,
}) => {
const [password, setPassword] = useState("");
diff --git a/site/src/pages/UserSettingsPage/TokensPage/TokensPageView.tsx b/site/src/pages/UserSettingsPage/TokensPage/TokensPageView.tsx
index 73a0560145..f370dc846e 100644
--- a/site/src/pages/UserSettingsPage/TokensPage/TokensPageView.tsx
+++ b/site/src/pages/UserSettingsPage/TokensPage/TokensPageView.tsx
@@ -1,4 +1,3 @@
-import { useTheme } from "@mui/styles";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
@@ -11,9 +10,10 @@ import { TableEmpty } from "components/TableEmpty/TableEmpty";
import { TableLoader } from "components/TableLoader/TableLoader";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import dayjs from "dayjs";
-import { FC } from "react";
+import { useTheme } from "@emotion/react";
+import { type FC, type ReactNode } from "react";
import IconButton from "@mui/material/IconButton/IconButton";
-import { APIKeyWithOwner } from "api/typesGenerated";
+import type { APIKeyWithOwner } from "api/typesGenerated";
import relativeTime from "dayjs/plugin/relativeTime";
import { ErrorAlert } from "components/Alert/ErrorAlert";
@@ -32,11 +32,10 @@ export interface TokensPageViewProps {
hasLoaded: boolean;
onDelete: (token: APIKeyWithOwner) => void;
deleteTokenError?: unknown;
+ children?: ReactNode;
}
-export const TokensPageView: FC<
- React.PropsWithChildren
-> = ({
+export const TokensPageView: FC = ({
tokens,
getTokensError,
isLoading,
diff --git a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx
index 25cce2e9f1..8aad4ef234 100644
--- a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx
+++ b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx
@@ -3,6 +3,7 @@ import { AvatarData } from "components/AvatarData/AvatarData";
import { Avatar } from "components/Avatar/Avatar";
import TableCell from "@mui/material/TableCell";
import TableRow from "@mui/material/TableRow";
+import { useTheme } from "@emotion/react";
import type { FC, ReactNode } from "react";
import {
HealthyBadge,
@@ -12,12 +13,15 @@ import {
} from "components/Badges/Badges";
import type { ProxyLatencyReport } from "contexts/useProxyLatency";
import { getLatencyColor } from "utils/latency";
-import Box from "@mui/material/Box";
-export const ProxyRow: FC<{
+interface ProxyRowProps {
latency?: ProxyLatencyReport;
proxy: Region;
-}> = ({ proxy, latency }) => {
+}
+
+export const ProxyRow: FC = ({ proxy, latency }) => {
+ const theme = useTheme();
+
// If we have a more specific proxy status, use that.
// All users can see healthy/unhealthy, some can see more.
let statusBadge = ;
@@ -56,16 +60,15 @@ export const ProxyRow: FC<{
/>
- {proxy.path_app_url}
- {statusBadge}
+ {proxy.path_app_url}
+ {statusBadge}
- latency
- ? getLatencyColor(theme, latency.latencyMS)
- : theme.palette.text.secondary,
+ color: latency
+ ? getLatencyColor(theme, latency.latencyMS)
+ : theme.palette.text.secondary,
}}
>
{latency ? `${latency.latencyMS.toFixed(0)} ms` : "Not available"}
@@ -75,7 +78,7 @@ export const ProxyRow: FC<{
@@ -85,30 +88,22 @@ export const ProxyRow: FC<{
);
};
-const ProxyMessagesRow: FC<{
+interface ProxyMessagesRowProps {
proxy: WorkspaceProxy;
-}> = ({ proxy }) => {
+}
+
+const ProxyMessagesRow: FC = ({ proxy }) => {
+ const theme = useTheme();
+
return (
<>
theme.palette.error.light }}
- >
- Errors
-
- }
+ title={Errors }
messages={proxy.status?.report?.errors}
/>
theme.palette.warning.light }}
- >
- Warnings
-
+ Warnings
}
messages={proxy.status?.report?.warnings}
/>
@@ -116,53 +111,58 @@ const ProxyMessagesRow: FC<{
);
};
-const ProxyMessagesList: FC<{
+interface ProxyMessagesListProps {
title: ReactNode;
messages?: string[];
-}> = ({ title, messages }) => {
+}
+
+const ProxyMessagesList: FC = ({ title, messages }) => {
+ const theme = useTheme();
+
if (!messages) {
return <>>;
}
return (
- `1px solid ${theme.palette.divider}`,
- backgroundColor: (theme) => theme.palette.background.default,
- p: "16px 24px",
+
-
+
{messages.map((error, index) => (
-
{error}
-
+
))}
-
+
);
};
-// DetailedProxyStatus allows a more precise status to be displayed.
-const DetailedProxyStatus: FC<{
+interface DetailedProxyStatusProps {
proxy: WorkspaceProxy;
-}> = ({ proxy }) => {
+}
+
+// DetailedProxyStatus allows a more precise status to be displayed.
+const DetailedProxyStatus: FC = ({ proxy }) => {
if (!proxy.status) {
// If the status is null/undefined/not provided, just go with the boolean "healthy" value.
return ;
@@ -187,10 +187,12 @@ const DetailedProxyStatus: FC<{
}
};
-// ProxyStatus will only show "healthy" or "not healthy" status.
-const ProxyStatus: FC<{
+interface ProxyStatusProps {
proxy: Region;
-}> = ({ proxy }) => {
+}
+
+// ProxyStatus will only show "healthy" or "not healthy" status.
+const ProxyStatus: FC = ({ proxy }) => {
let icon = ;
if (proxy.healthy) {
icon = ;
diff --git a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx
index 21037635de..0832ef8dcf 100644
--- a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx
+++ b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx
@@ -47,7 +47,7 @@ export const WorkspaceProxyView: FC<
Proxy
URL
Status
-
+
Latency
diff --git a/site/src/pages/UsersPage/UsersFilter.tsx b/site/src/pages/UsersPage/UsersFilter.tsx
index 366900690b..e01191346d 100644
--- a/site/src/pages/UsersPage/UsersFilter.tsx
+++ b/site/src/pages/UsersPage/UsersFilter.tsx
@@ -1,6 +1,5 @@
-import { FC } from "react";
-import Box from "@mui/material/Box";
-import { Palette, PaletteColor } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
+import { type FC } from "react";
import {
Filter,
FilterMenu,
@@ -9,8 +8,12 @@ import {
SearchFieldSkeleton,
useFilter,
} from "components/Filter/filter";
-import { BaseOption } from "components/Filter/options";
-import { UseFilterMenuOptions, useFilterMenu } from "components/Filter/menu";
+import type { BaseOption } from "components/Filter/options";
+import {
+ type UseFilterMenuOptions,
+ useFilterMenu,
+} from "components/Filter/menu";
+import type { ThemeRole } from "theme/experimental";
import { docs } from "utils/docs";
const userFilterQuery = {
@@ -19,7 +22,7 @@ const userFilterQuery = {
};
type StatusOption = BaseOption & {
- color: string;
+ color: ThemeRole;
};
export const useStatusFilterMenu = ({
@@ -28,7 +31,7 @@ export const useStatusFilterMenu = ({
}: Pick, "value" | "onChange">) => {
const statusOptions: StatusOption[] = [
{ value: "active", label: "Active", color: "success" },
- { value: "dormant", label: "Dormant", color: "secondary" },
+ { value: "dormant", label: "Dormant", color: "notice" },
{ value: "suspended", label: "Suspended", color: "warning" },
];
return useFilterMenu({
@@ -48,17 +51,15 @@ const PRESET_FILTERS = [
{ query: userFilterQuery.all, name: "All users" },
];
-export const UsersFilter = ({
- filter,
- error,
- menus,
-}: {
+interface UsersFilterProps {
filter: ReturnType;
error?: unknown;
menus: {
status: StatusFilterMenu;
};
-}) => {
+}
+
+export const UsersFilter: FC = ({ filter, error, menus }) => {
return (
{
);
};
-const StatusOptionItem = ({
- option,
- isSelected,
-}: {
+interface StatusOptionItemProps {
option: StatusOption;
isSelected?: boolean;
+}
+
+const StatusOptionItem: FC = ({
+ option,
+ isSelected,
}) => {
return (
= ({ option }) => {
+interface StatusIndicatorProps {
+ option: StatusOption;
+}
+
+const StatusIndicator: FC = ({ option }) => {
+ const theme = useTheme();
+
return (
-
- (theme.palette[option.color as keyof Palette] as PaletteColor).light,
+
);
diff --git a/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx b/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx
index e3deb15f48..695f3e3440 100644
--- a/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx
+++ b/site/src/pages/UsersPage/UsersTable/UserGroupsCell.tsx
@@ -51,7 +51,7 @@ export function UserGroupsCell({ userGroups }: GroupsCellProps) {
css={{ columnGap: 8, alignItems: "center" }}
>
0 ? 0.8 : 0.5,
diff --git a/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx b/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx
index 17cdf3226e..af7e0ea3cc 100644
--- a/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx
+++ b/site/src/pages/UsersPage/UsersTable/UsersTableBody.tsx
@@ -1,12 +1,18 @@
-import Box from "@mui/material/Box";
import TableCell from "@mui/material/TableCell";
import TableRow from "@mui/material/TableRow";
import Skeleton from "@mui/material/Skeleton";
+import Divider from "@mui/material/Divider";
+import HideSourceOutlined from "@mui/icons-material/HideSourceOutlined";
+import KeyOutlined from "@mui/icons-material/KeyOutlined";
+import GitHub from "@mui/icons-material/GitHub";
+import PasswordOutlined from "@mui/icons-material/PasswordOutlined";
+import ShieldOutlined from "@mui/icons-material/ShieldOutlined";
import { type Interpolation, type Theme } from "@emotion/react";
import { type FC } from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import type * as TypesGen from "api/typesGenerated";
+import { type GroupsByUserId } from "api/queries/groups";
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
import { AvatarData } from "components/AvatarData/AvatarData";
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
@@ -16,15 +22,7 @@ import {
TableRowSkeleton,
} from "components/TableLoader/TableLoader";
import { EnterpriseBadge } from "components/Badges/Badges";
-import HideSourceOutlined from "@mui/icons-material/HideSourceOutlined";
-import KeyOutlined from "@mui/icons-material/KeyOutlined";
-import GitHub from "@mui/icons-material/GitHub";
-import PasswordOutlined from "@mui/icons-material/PasswordOutlined";
-import ShieldOutlined from "@mui/icons-material/ShieldOutlined";
import { LastSeen } from "components/LastSeen/LastSeen";
-import { UserRoleCell } from "./UserRoleCell";
-import { type GroupsByUserId } from "api/queries/groups";
-import { UserGroupsCell } from "./UserGroupsCell";
import {
MoreMenu,
MoreMenuTrigger,
@@ -32,7 +30,8 @@ import {
MoreMenuItem,
ThreeDotsButton,
} from "components/MoreMenu/MoreMenu";
-import Divider from "@mui/material/Divider";
+import { UserRoleCell } from "./UserRoleCell";
+import { UserGroupsCell } from "./UserGroupsCell";
dayjs.extend(relativeTime);
@@ -91,9 +90,9 @@ export const UsersTableBody: FC<
-
+
@@ -126,9 +125,9 @@ export const UsersTableBody: FC<
-
+
-
+
@@ -136,9 +135,9 @@ export const UsersTableBody: FC<
-
+
-
+
@@ -177,8 +176,8 @@ export const UsersTableBody: FC<
user.status === "suspended" && styles.suspended,
]}
>
- {user.status}
-
+ {user.status}
+
{canEditUsers && (
@@ -237,57 +236,60 @@ export const UsersTableBody: FC<
);
};
-const LoginType = ({
- authMethods,
- value,
-}: {
+interface LoginTypeProps {
authMethods: TypesGen.AuthMethods;
value: TypesGen.LoginType;
-}) => {
+}
+
+const LoginType: FC = ({ authMethods, value }) => {
let displayName: string = value;
let icon = <>>;
- const iconStyles = { width: 14, height: 14 };
if (value === "password") {
displayName = "Password";
- icon = ;
+ icon = ;
} else if (value === "none") {
displayName = "None";
- icon = ;
+ icon = ;
} else if (value === "github") {
displayName = "GitHub";
- icon = ;
+ icon = ;
} else if (value === "token") {
displayName = "Token";
- icon = ;
+ icon = ;
} else if (value === "oidc") {
displayName =
authMethods.oidc.signInText === "" ? "OIDC" : authMethods.oidc.signInText;
icon =
authMethods.oidc.iconUrl === "" ? (
-
+
) : (
-
);
}
return (
-
+
{icon}
{displayName}
-
+
);
};
const styles = {
+ icon: {
+ width: 14,
+ height: 14,
+ },
+
status: {
textTransform: "capitalize",
},
+
suspended: (theme) => ({
color: theme.palette.text.secondary,
}),
diff --git a/site/src/pages/WorkspacePage/BuildsTable.tsx b/site/src/pages/WorkspacePage/BuildsTable.tsx
index 2cf4dd5524..3063796791 100644
--- a/site/src/pages/WorkspacePage/BuildsTable.tsx
+++ b/site/src/pages/WorkspacePage/BuildsTable.tsx
@@ -1,31 +1,31 @@
-import Box from "@mui/material/Box";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableRow from "@mui/material/TableRow";
-import { Timeline } from "components/Timeline/Timeline";
-import { FC } from "react";
-import * as TypesGen from "api/typesGenerated";
-import { EmptyState } from "components/EmptyState/EmptyState";
-import { TableLoader } from "components/TableLoader/TableLoader";
-import { BuildRow } from "./BuildRow";
-import { Stack } from "components/Stack/Stack";
import LoadingButton from "@mui/lab/LoadingButton";
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";
+import { type FC, type ReactNode } from "react";
+import type * as TypesGen from "api/typesGenerated";
+import { EmptyState } from "components/EmptyState/EmptyState";
+import { TableLoader } from "components/TableLoader/TableLoader";
+import { Timeline } from "components/Timeline/Timeline";
+import { Stack } from "components/Stack/Stack";
+import { BuildRow } from "./BuildRow";
export const Language = {
emptyMessage: "No builds found",
};
export interface BuildsTableProps {
+ children?: ReactNode;
builds: TypesGen.WorkspaceBuild[] | undefined;
onLoadMoreBuilds: () => void;
isLoadingMoreBuilds: boolean;
hasMoreBuilds: boolean;
}
-export const BuildsTable: FC> = ({
+export const BuildsTable: FC = ({
builds,
onLoadMoreBuilds,
isLoadingMoreBuilds,
@@ -49,9 +49,9 @@ export const BuildsTable: FC> = ({
{builds && builds.length === 0 && (
-
+
-
+
)}
diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx
index fcda04c5c0..f7756a2c8c 100644
--- a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx
@@ -1,9 +1,10 @@
import ExpandMoreOutlined from "@mui/icons-material/ExpandMoreOutlined";
-import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
+import { useTheme } from "@emotion/react";
+import { type FC } from "react";
import { useQuery } from "react-query";
import { getWorkspaceParameters } from "api/api";
-import {
+import type {
TemplateVersionParameter,
Workspace,
WorkspaceBuildParameter,
@@ -28,14 +29,16 @@ import {
usePopover,
} from "components/Popover/Popover";
-export const BuildParametersPopover = ({
- workspace,
- disabled,
- onSubmit,
-}: {
+interface BuildParametersPopoverProps {
workspace: Workspace;
disabled?: boolean;
onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void;
+}
+
+export const BuildParametersPopover: FC = ({
+ workspace,
+ disabled,
+ onSubmit,
}) => {
const { data: parameters } = useQuery({
queryKey: ["workspace", workspace.id, "parameters"],
@@ -52,9 +55,9 @@ export const BuildParametersPopover = ({
data-testid="build-parameters-button"
disabled={disabled}
color="neutral"
- sx={{ px: 0 }}
+ css={{ paddingLeft: 0, paddingRight: 0 }}
>
-
+
void;
+interface BuildParametersPopoverContentProps {
ephemeralParameters?: TemplateVersionParameter[];
buildParameters?: WorkspaceBuildParameter[];
+ onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void;
+}
+
+const BuildParametersPopoverContent: FC = ({
+ ephemeralParameters,
+ buildParameters,
+ onSubmit,
}) => {
+ const theme = useTheme();
const popover = usePopover();
return (
@@ -87,19 +93,19 @@ const BuildParametersPopoverContent = ({
{buildParameters && ephemeralParameters ? (
ephemeralParameters.length > 0 ? (
<>
- theme.palette.text.secondary,
- p: 2.5,
- borderBottom: (theme) => `1px solid ${theme.palette.divider}`,
+
Build Options
These parameters only apply for a single workspace start.
-
-
+
+
>
) : (
- theme.palette.text.secondary,
- p: 2.5,
- borderBottom: (theme) => `1px solid ${theme.palette.divider}`,
+
Build Options
@@ -129,7 +135,7 @@ const BuildParametersPopoverContent = ({
Read the docs
-
+
)
) : (
@@ -138,14 +144,16 @@ const BuildParametersPopoverContent = ({
);
};
-const Form = ({
- ephemeralParameters,
- buildParameters,
- onSubmit,
-}: {
+interface FormProps {
ephemeralParameters: TemplateVersionParameter[];
buildParameters: WorkspaceBuildParameter[];
onSubmit: (buildParameters: WorkspaceBuildParameter[]) => void;
+}
+
+const Form: FC = ({
+ ephemeralParameters,
+ buildParameters,
+ onSubmit,
}) => {
const form = useFormik({
initialValues: {
@@ -180,17 +188,17 @@ const Form = ({
);
})}
-
+
Build workspace
-
+
);
};
diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx
index 33456d6bf2..0e95000a85 100644
--- a/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx
@@ -1,7 +1,3 @@
-import { type FC } from "react";
-import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
-import { BuildParametersPopover } from "./BuildParametersPopover";
-
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import LoadingButton from "@mui/lab/LoadingButton";
@@ -15,15 +11,18 @@ import OutlinedBlockIcon from "@mui/icons-material/BlockOutlined";
import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew";
import RetryIcon from "@mui/icons-material/BuildOutlined";
import RetryDebugIcon from "@mui/icons-material/BugReportOutlined";
+import { type FC } from "react";
+import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
+import { BuildParametersPopover } from "./BuildParametersPopover";
-interface WorkspaceActionProps {
+interface ActionButtonProps {
loading?: boolean;
- handleAction: () => void;
+ handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
disabled?: boolean;
tooltipText?: string;
}
-export const UpdateButton: FC = ({
+export const UpdateButton: FC = ({
handleAction,
loading,
}) => {
@@ -33,14 +32,14 @@ export const UpdateButton: FC = ({
loadingPosition="start"
data-testid="workspace-update-button"
startIcon={ }
- onClick={handleAction}
+ onClick={() => handleAction()}
>
{loading ? <>Updating…> : <>Update…>}
);
};
-export const ActivateButton: FC = ({
+export const ActivateButton: FC = ({
handleAction,
loading,
}) => {
@@ -49,19 +48,24 @@ export const ActivateButton: FC = ({
loading={loading}
loadingPosition="start"
startIcon={ }
- onClick={handleAction}
+ onClick={() => handleAction()}
>
{loading ? <>Activating…> : "Activate"}
);
};
-export const StartButton: FC<
- Omit & {
- workspace: Workspace;
- handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
- }
-> = ({ handleAction, workspace, loading, disabled, tooltipText }) => {
+interface ActionButtonPropsWithWorkspace extends ActionButtonProps {
+ workspace: Workspace;
+}
+
+export const StartButton: FC = ({
+ handleAction,
+ workspace,
+ loading,
+ disabled,
+ tooltipText,
+}) => {
const buttonContent = (
= ({
+export const StopButton: FC = ({
handleAction,
loading,
}) => {
@@ -106,7 +110,7 @@ export const StopButton: FC = ({
loading={loading}
loadingPosition="start"
startIcon={ }
- onClick={handleAction}
+ onClick={() => handleAction()}
data-testid="workspace-stop-button"
>
{loading ? <>Stopping…> : "Stop"}
@@ -114,16 +118,17 @@ export const StopButton: FC = ({
);
};
-export const RestartButton: FC<
- Omit & {
- workspace: Workspace;
- handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
- }
-> = ({ handleAction, loading, workspace, disabled, tooltipText }) => {
+export const RestartButton: FC = ({
+ handleAction,
+ loading,
+ workspace,
+ disabled,
+ tooltipText,
+}) => {
const buttonContent = (
button:hover + button": {
borderLeft: "1px solid #FFF",
@@ -156,19 +161,19 @@ export const RestartButton: FC<
);
};
-export const CancelButton: FC = ({ handleAction }) => {
+export const CancelButton: FC = ({ handleAction }) => {
return (
- } onClick={handleAction}>
+ } onClick={() => handleAction()}>
Cancel
);
};
-interface DisabledProps {
+interface DisabledButtonProps {
label: string;
}
-export const DisabledButton: FC = ({ label }) => {
+export const DisabledButton: FC = ({ label }) => {
return (
} disabled>
{label}
@@ -188,18 +193,18 @@ export const ActionLoadingButton: FC = ({ label }) => {
);
};
-type DebugButtonProps = Omit & {
+type RetryButtonProps = Omit & {
debug?: boolean;
};
-export const RetryButton = ({
+export const RetryButton: FC = ({
handleAction,
debug = false,
-}: DebugButtonProps) => {
+}) => {
return (
: }
- onClick={handleAction}
+ onClick={() => handleAction()}
>
Retry{debug && " (Debug)"}
diff --git a/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx b/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx
index a45f01c793..ceb491277a 100644
--- a/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceBuildLogsSection.tsx
@@ -1,15 +1,18 @@
-import Box from "@mui/material/Box";
-import { ProvisionerJobLog } from "api/typesGenerated";
+import { useTheme } from "@emotion/react";
+import { type FC, useRef, useEffect } from "react";
+import type { ProvisionerJobLog } from "api/typesGenerated";
import { Loader } from "components/Loader/Loader";
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs";
-import { useRef, useEffect } from "react";
-export const WorkspaceBuildLogsSection = ({
+interface WorkspaceBuildLogsSectionProps {
+ logs?: ProvisionerJobLog[];
+}
+
+export const WorkspaceBuildLogsSection: FC = ({
logs,
-}: {
- logs: ProvisionerJobLog[] | undefined;
}) => {
const scrollRef = useRef(null);
+ const theme = useTheme();
useEffect(() => {
// Auto scrolling makes hard to snapshot test using Chromatic
@@ -24,14 +27,15 @@ export const WorkspaceBuildLogsSection = ({
}, [logs]);
return (
- ({
- borderRadius: 1,
+
-
({
+
-
({
- height: "400px",
- overflowY: "auto",
- })}
- >
+
+
{logs ? (
) : (
-
-
+
)}
-
-
+
+
);
};
diff --git a/site/src/pages/WorkspacePage/WorkspacePage.tsx b/site/src/pages/WorkspacePage/WorkspacePage.tsx
index b11cfb1a1a..dfc124d250 100644
--- a/site/src/pages/WorkspacePage/WorkspacePage.tsx
+++ b/site/src/pages/WorkspacePage/WorkspacePage.tsx
@@ -103,7 +103,10 @@ export const WorkspacePage: FC = () => {
if (pageError) {
return (
-
+
);
}
diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx
index caf2d3bb88..1096824318 100644
--- a/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx
+++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceParametersPage/WorkspaceParametersPage.tsx
@@ -10,7 +10,7 @@ import {
} from "./WorkspaceParametersForm";
import { useNavigate } from "react-router-dom";
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
-import { FC } from "react";
+import { type FC } from "react";
import { isApiValidationError } from "api/errors";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { WorkspaceBuildParameter } from "api/typesGenerated";
@@ -19,7 +19,7 @@ import Button from "@mui/material/Button";
import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined";
import { docs } from "utils/docs";
-const WorkspaceParametersPage = () => {
+const WorkspaceParametersPage: FC = () => {
const workspace = useWorkspaceSettings();
const parameters = useQuery({
queryKey: ["workspace", workspace.id, "parameters"],
@@ -79,16 +79,12 @@ export const WorkspaceParametersPageView: FC<
> = ({ data, submitError, isSubmitting, onSubmit, onCancel }) => {
return (
<>
-
+
Workspace parameters
{submitError && !isApiValidationError(submitError) && (
-
+
)}
{data ? (
diff --git a/site/src/pages/WorkspacesPage/BatchActions.tsx b/site/src/pages/WorkspacesPage/BatchActions.tsx
index 6209b088f8..d1c9bc43a5 100644
--- a/site/src/pages/WorkspacesPage/BatchActions.tsx
+++ b/site/src/pages/WorkspacesPage/BatchActions.tsx
@@ -1,16 +1,18 @@
+import { useTheme } from "@emotion/react";
import TextField from "@mui/material/TextField";
-import { Box } from "@mui/system";
import { deleteWorkspace, startWorkspace, stopWorkspace } from "api/api";
-import { Workspace } from "api/typesGenerated";
+import type { Workspace } from "api/typesGenerated";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { displayError } from "components/GlobalSnackbar/utils";
-import { useState } from "react";
+import { type FC, useState } from "react";
import { useMutation } from "react-query";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
-export const useBatchActions = (options: {
+interface UseBatchActionsProps {
onSuccess: () => Promise;
-}) => {
+}
+
+export function useBatchActions(options: UseBatchActionsProps) {
const { onSuccess } = options;
const startAllMutation = useMutation({
@@ -56,7 +58,7 @@ export const useBatchActions = (options: {
stopAllMutation.isLoading ||
deleteAllMutation.isLoading,
};
-};
+}
type BatchDeleteConfirmationProps = {
checkedWorkspaces: Workspace[];
@@ -66,10 +68,11 @@ type BatchDeleteConfirmationProps = {
onConfirm: () => void;
};
-export const BatchDeleteConfirmation = (
- props: BatchDeleteConfirmationProps,
+export const BatchDeleteConfirmation: FC = (
+ props,
) => {
const { checkedWorkspaces, open, onClose, onConfirm, isLoading } = props;
+ const theme = useTheme();
const [confirmation, setConfirmation] = useState({ value: "", error: false });
const confirmDeletion = () => {
@@ -103,21 +106,20 @@ export const BatchDeleteConfirmation = (
confirmDeletion();
}}
>
-
+
Deleting these workspaces is irreversible! Are you sure you want to
proceed? Type{" "}
- theme.palette.text.primary,
+ color: theme.palette.text.primary,
fontWeight: 600,
}}
>
`DELETE`
- {" "}
+ {" "}
to confirm.
-
+
{
const value = e.currentTarget?.value;
setConfirmation((c) => ({ ...c, value }));
diff --git a/site/src/pages/WorkspacesPage/WorkspacesButton.tsx b/site/src/pages/WorkspacesPage/WorkspacesButton.tsx
index cbbd8e63fa..a50510a0cc 100644
--- a/site/src/pages/WorkspacesPage/WorkspacesButton.tsx
+++ b/site/src/pages/WorkspacesPage/WorkspacesButton.tsx
@@ -1,11 +1,10 @@
-import { type PropsWithChildren, type ReactNode, useState } from "react";
+import { type FC, type ReactNode, useState } from "react";
import { type Template } from "api/typesGenerated";
import { type UseQueryResult } from "react-query";
import {
Link as RouterLink,
LinkProps as RouterLinkProps,
} from "react-router-dom";
-import Box from "@mui/system/Box";
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import AddIcon from "@mui/icons-material/AddOutlined";
@@ -25,16 +24,17 @@ const ICON_SIZE = 18;
type TemplatesQuery = UseQueryResult;
-type WorkspacesButtonProps = PropsWithChildren<{
+interface WorkspacesButtonProps {
+ children?: ReactNode;
templatesFetchStatus: TemplatesQuery["status"];
templates: TemplatesQuery["data"];
-}>;
+}
-export function WorkspacesButton({
+export const WorkspacesButton: FC = ({
children,
templatesFetchStatus,
templates,
-}: WorkspacesButtonProps) {
+}) => {
// Dataset should always be small enough that client-side filtering should be
// good enough. Can swap out down the line if it becomes an issue
const [searchTerm, setSearchTerm] = useState("");
@@ -69,15 +69,16 @@ export function WorkspacesButton({
onValueChange={(newValue) => setSearchTerm(newValue)}
placeholder="Type/select a workspace template"
label="Template select for workspace"
- sx={{ flexShrink: 0, columnGap: 1.5 }}
+ css={{ flexShrink: 0, columnGap: 12 }}
/>
{templatesFetchStatus === "loading" ? (
@@ -93,7 +94,7 @@ export function WorkspacesButton({
)}
- ({
padding: "8px 0",
borderTop: `1px solid ${theme.palette.divider}`,
@@ -105,20 +106,23 @@ export function WorkspacesButton({
display: "flex",
alignItems: "center",
columnGap: 12,
-
color: theme.palette.primary.main,
})}
>
See all templates
-
+
);
+};
+
+interface WorkspaceResultsRowProps {
+ template: Template;
}
-function WorkspaceResultsRow({ template }: { template: Template }) {
+const WorkspaceResultsRow: FC = ({ template }) => {
return (
- ({
color: theme.palette.text.primary,
display: "flex",
@@ -170,15 +174,15 @@ function WorkspaceResultsRow({ template }: { template: Template }) {
developer
{template.active_user_count === 1 ? "" : "s"}
-
+
);
-}
+};
-function PopoverLink(props: RouterLinkProps) {
+const PopoverLink: FC = ({ children, ...linkProps }) => {
return (
({
color: theme.palette.text.primary,
padding: "8px 16px",
@@ -193,9 +197,11 @@ function PopoverLink(props: RouterLinkProps) {
backgroundColor: theme.palette.action.hover,
},
})}
- />
+ >
+ {children}
+
);
-}
+};
function sortTemplatesByUsersDesc(
templates: readonly Template[],
diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
index fe096ef9c0..c68e1cdfbc 100644
--- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
+++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
@@ -12,7 +12,6 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { WorkspacesFilter } from "./filter/filter";
import { hasError, isApiValidationError } from "api/errors";
import { TableToolbar } from "components/TableToolbar/TableToolbar";
-import Box from "@mui/material/Box";
import DeleteOutlined from "@mui/icons-material/DeleteOutlined";
import { WorkspacesButton } from "./WorkspacesButton";
import { UseQueryResult } from "react-query";
@@ -136,11 +135,11 @@ export const WorkspacesPageView = ({
{checkedWorkspaces.length > 0 ? (
<>
-
+
Selected {checkedWorkspaces.length} of{" "}
{workspaces?.length} {" "}
{workspaces?.length === 1 ? "workspace" : "workspaces"}
-
+
diff --git a/site/src/pages/WorkspacesPage/WorkspacesSearchBox.tsx b/site/src/pages/WorkspacesPage/WorkspacesSearchBox.tsx
index 297ae5699f..d9c8a8ab0d 100644
--- a/site/src/pages/WorkspacesPage/WorkspacesSearchBox.tsx
+++ b/site/src/pages/WorkspacesPage/WorkspacesSearchBox.tsx
@@ -7,88 +7,86 @@
import {
type ForwardedRef,
type KeyboardEvent,
+ type InputHTMLAttributes,
forwardRef,
useId,
} from "react";
-
-import Box from "@mui/system/Box";
import SearchIcon from "@mui/icons-material/SearchOutlined";
import { visuallyHidden } from "@mui/utils";
-import { type SystemStyleObject } from "@mui/system";
+import { useTheme } from "@emotion/react";
-type Props = {
- value: string;
- onValueChange: (newValue: string) => void;
-
- placeholder?: string;
+interface SearchBoxProps extends InputHTMLAttributes {
label?: string;
+ value: string;
onKeyDown?: (event: KeyboardEvent) => void;
- sx?: SystemStyleObject;
-};
+ onValueChange: (newValue: string) => void;
+}
export const SearchBox = forwardRef(function SearchBox(
- {
- value,
+ props: SearchBoxProps,
+ ref?: ForwardedRef,
+) {
+ const {
onValueChange,
onKeyDown,
label = "Search",
placeholder = "Search...",
- sx = {},
- }: Props,
- ref?: ForwardedRef,
-) {
+ ...attrs
+ } = props;
+
const hookId = useId();
+ const theme = useTheme();
+
const inputId = `${hookId}-${SearchBox.name}-input`;
return (
- `1px solid ${theme.palette.divider}`,
- ...sx,
+ borderBottom: `1px solid ${theme.palette.divider}`,
}}
- onKeyDown={onKeyDown}
>
-
+
theme.palette.text.secondary,
+ marginLeft: "auto",
+ marginRight: "auto",
+ color: theme.palette.text.secondary,
}}
/>
-
+
-
+
{label}
-
+
- onValueChange(e.target.value)}
- sx={{
+ css={{
height: "100%",
border: 0,
background: "none",
width: "100%",
outline: 0,
"&::placeholder": {
- color: (theme) => theme.palette.text.secondary,
+ color: theme.palette.text.secondary,
},
}}
/>
-
+
);
});
diff --git a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx
index 689b857509..c08563f8bc 100644
--- a/site/src/pages/WorkspacesPage/WorkspacesTable.tsx
+++ b/site/src/pages/WorkspacesPage/WorkspacesTable.tsx
@@ -4,16 +4,18 @@ import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
-import { Template, Workspace } from "api/typesGenerated";
-import { FC, ReactNode } from "react";
+import Checkbox from "@mui/material/Checkbox";
+import Skeleton from "@mui/material/Skeleton";
+import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight";
+import { useTheme } from "@emotion/react";
+import { type FC, type ReactNode } from "react";
+import { useNavigate } from "react-router-dom";
+import type { Template, Workspace } from "api/typesGenerated";
import {
TableLoaderSkeleton,
TableRowSkeleton,
} from "components/TableLoader/TableLoader";
-import { useNavigate } from "react-router-dom";
import { useClickableTableRow } from "hooks/useClickableTableRow";
-import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight";
-import Box from "@mui/material/Box";
import { AvatarData } from "components/AvatarData/AvatarData";
import { Avatar } from "components/Avatar/Avatar";
import { Stack } from "components/Stack/Stack";
@@ -21,9 +23,7 @@ import { LastUsed } from "pages/WorkspacesPage/LastUsed";
import { WorkspaceOutdatedTooltip } from "components/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip";
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge";
import { getDisplayWorkspaceTemplateName } from "utils/workspace";
-import Checkbox from "@mui/material/Checkbox";
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
-import Skeleton from "@mui/material/Skeleton";
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
import { WorkspacesEmpty } from "./WorkspacesEmpty";
@@ -49,18 +49,20 @@ export const WorkspacesTable: FC = ({
templates,
canCreateTemplate,
}) => {
+ const theme = useTheme();
+
return (
-
+
{canCheckWorkspaces && (
= ({
/>
)}
Name
-
+
Template
Last used
@@ -109,12 +111,14 @@ export const WorkspacesTable: FC = ({
checked={checked}
>
-
+
{canCheckWorkspaces && (
= ({
}
/>
-
+
@@ -181,7 +185,9 @@ export const WorkspacesTable: FC = ({
-
+
{workspace.latest_build.status === "running" &&
!workspace.health.healthy && (
@@ -191,19 +197,19 @@ export const WorkspacesTable: FC = ({
message="Your workspace is running but some agents are unhealthy."
/>
)}
-
+
-
+
theme.palette.text.secondary,
+ css={{
+ color: theme.palette.text.secondary,
width: 20,
height: 20,
}}
/>
-
+
);
@@ -214,12 +220,19 @@ export const WorkspacesTable: FC = ({
);
};
-const WorkspacesRow: FC<{
+interface WorkspacesRowProps {
workspace: Workspace;
- children: ReactNode;
+ children?: ReactNode;
checked: boolean;
-}> = ({ workspace, children, checked }) => {
+}
+
+const WorkspacesRow: FC = ({
+ workspace,
+ children,
+ checked,
+}) => {
const navigate = useNavigate();
+ const theme = useTheme();
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`;
const openLinkInNewTab = () => window.open(workspacePageLink, "_blank");
@@ -245,9 +258,8 @@ const WorkspacesRow: FC<{
- checked ? theme.palette.action.hover : undefined,
+ css={{
+ backgroundColor: checked ? theme.palette.action.hover : undefined,
}}
>
{children}
@@ -255,21 +267,21 @@ const WorkspacesRow: FC<{
);
};
-const TableLoader = ({
- canCheckWorkspaces,
-}: {
- canCheckWorkspaces: boolean;
-}) => {
+interface TableLoaderProps {
+ canCheckWorkspaces?: boolean;
+}
+
+const TableLoader: FC = ({ canCheckWorkspaces }) => {
return (
-
+
{canCheckWorkspaces && (
-
+
)}
-
+
diff --git a/site/src/pages/WorkspacesPage/filter/filter.tsx b/site/src/pages/WorkspacesPage/filter/filter.tsx
index 2d7a6d6b04..3bc7c33240 100644
--- a/site/src/pages/WorkspacesPage/filter/filter.tsx
+++ b/site/src/pages/WorkspacesPage/filter/filter.tsx
@@ -1,10 +1,9 @@
-import { FC } from "react";
-import Box from "@mui/material/Box";
+import { type FC } from "react";
+import { useTheme } from "@emotion/react";
import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider";
-import { Avatar, AvatarProps } from "components/Avatar/Avatar";
-import { Palette, PaletteColor } from "@mui/material/styles";
-import { TemplateFilterMenu, StatusFilterMenu } from "./menus";
-import { TemplateOption, StatusOption } from "./options";
+import { Avatar, type AvatarProps } from "components/Avatar/Avatar";
+import type { TemplateFilterMenu, StatusFilterMenu } from "./menus";
+import type { TemplateOption, StatusOption } from "./options";
import {
Filter,
FilterMenu,
@@ -14,7 +13,7 @@ import {
SearchFieldSkeleton,
useFilter,
} from "components/Filter/filter";
-import { UserFilterMenu, UserMenu } from "components/Filter/UserFilter";
+import { type UserFilterMenu, UserMenu } from "components/Filter/UserFilter";
import { docs } from "utils/docs";
export const workspaceFilterQuery = {
@@ -70,11 +69,11 @@ type WorkspaceFilterProps = {
};
};
-export const WorkspacesFilter = ({
+export const WorkspacesFilter: FC = ({
filter,
error,
menus,
-}: WorkspaceFilterProps) => {
+}) => {
const actionsEnabled = useIsWorkspaceActionsEnabled();
const presets = actionsEnabled ? PRESETS_WITH_DORMANT : PRESET_FILTERS;
@@ -87,7 +86,7 @@ export const WorkspacesFilter = ({
learnMoreLink={docs("/workspaces#workspace-filtering")}
options={
<>
- {menus.user && }
+ {menus.user && }
>
@@ -122,12 +121,14 @@ const TemplateMenu = (menu: TemplateFilterMenu) => {
);
};
-const TemplateOptionItem = ({
- option,
- isSelected,
-}: {
+interface TemplateOptionItemProps {
option: TemplateOption;
isSelected?: boolean;
+}
+
+const TemplateOptionItem: FC = ({
+ option,
+ isSelected,
}) => {
return (
}
/>
);
};
-const TemplateAvatar: FC<
- AvatarProps & { templateName: string; icon?: string }
-> = ({ templateName, icon, ...avatarProps }) => {
+interface TemplateAvatarProps extends AvatarProps {
+ templateName: string;
+ icon?: string;
+}
+
+const TemplateAvatar: FC = ({
+ templateName,
+ icon,
+ ...avatarProps
+}) => {
return icon ? (
) : (
@@ -172,13 +180,12 @@ const StatusMenu = (menu: StatusFilterMenu) => {
);
};
-const StatusOptionItem = ({
- option,
- isSelected,
-}: {
+interface StatusOptionItem {
option: StatusOption;
isSelected?: boolean;
-}) => {
+}
+
+const StatusOptionItem: FC = ({ option, isSelected }) => {
return (
= ({ option }) => {
- const color = option.color === "notice" ? "warning" : option.color;
+interface StatusIndicatorProps {
+ option: StatusOption;
+}
+
+const StatusIndicator: FC = ({ option }) => {
+ const theme = useTheme();
return (
- {
- return {
- backgroundColor: (
- theme.palette[color as keyof Palette] as PaletteColor
- ).light,
- };
+
);
diff --git a/site/src/pages/WorkspacesPage/filter/options.ts b/site/src/pages/WorkspacesPage/filter/options.ts
index 5266e4cdb9..dcdbd99984 100644
--- a/site/src/pages/WorkspacesPage/filter/options.ts
+++ b/site/src/pages/WorkspacesPage/filter/options.ts
@@ -1,7 +1,8 @@
import { BaseOption } from "components/Filter/options";
+import type { ThemeRole } from "theme/experimental";
export type StatusOption = BaseOption & {
- color: string;
+ color: ThemeRole;
};
export type TemplateOption = BaseOption & {
diff --git a/site/src/testHelpers/renderHelpers.tsx b/site/src/testHelpers/renderHelpers.tsx
index b9d3fcdde5..7cb1ad5400 100644
--- a/site/src/testHelpers/renderHelpers.tsx
+++ b/site/src/testHelpers/renderHelpers.tsx
@@ -122,13 +122,9 @@ type RenderHookWithAuthOptions = Partial<
*/
export async function renderHookWithAuth(
render: (initialProps: Props) => Result,
- {
- initialProps,
- path = "/",
- route = "/",
- extraRoutes = [],
- }: RenderHookWithAuthOptions = {},
+ options: RenderHookWithAuthOptions = {},
) {
+ const { initialProps, path = "/", route = "/", extraRoutes = [] } = options;
const queryClient = createTestQueryClient();
// Easy to miss – there's an evil definite assignment via the !