diff --git a/site/src/components/IconField/IconField.tsx b/site/src/components/IconField/IconField.tsx index 6e8bf11506cfa..c1e26fe4acdc7 100644 --- a/site/src/components/IconField/IconField.tsx +++ b/site/src/components/IconField/IconField.tsx @@ -1,9 +1,9 @@ +import { css, Global, useTheme } from "@emotion/react"; import Button from "@mui/material/Button"; import InputAdornment from "@mui/material/InputAdornment"; -import TextField, { TextFieldProps } from "@mui/material/TextField"; -import { makeStyles } from "@mui/styles"; +import TextField, { type TextFieldProps } from "@mui/material/TextField"; import Picker from "@emoji-mart/react"; -import { FC } from "react"; +import { type FC } from "react"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { Stack } from "components/Stack/Stack"; import { colors } from "theme/colors"; @@ -48,7 +48,7 @@ const IconField: FC = ({ onPickEmoji, ...textFieldProps }) => { throw new Error(`Invalid icon value "${typeof textFieldProps.value}"`); } - const styles = useStyles(); + const theme = useTheme(); const hasIcon = textFieldProps.value && textFieldProps.value !== ""; return ( @@ -59,7 +59,21 @@ const IconField: FC = ({ onPickEmoji, ...textFieldProps }) => { label="Icon" InputProps={{ endAdornment: hasIcon ? ( - + = ({ onPickEmoji, ...textFieldProps }) => { id="emoji" css={{ marginTop: 0, ".MuiPaper-root": { width: "auto" } }} > + = ({ onPickEmoji, ...textFieldProps }) => { ); }; -const useStyles = makeStyles((theme) => ({ - "@global": { - "em-emoji-picker": { - "--rgb-background": theme.palette.background.paper, - "--rgb-input": colors.gray[17], - "--rgb-color": colors.gray[4], - - // Hack to prevent the right side from being cut off - width: 350, - }, - }, - adornment: { - width: theme.spacing(3), - height: theme.spacing(3), - display: "flex", - alignItems: "center", - justifyContent: "center", - - "& img": { - maxWidth: "100%", - objectFit: "contain", - }, - }, -})); - export default IconField; diff --git a/site/src/components/TableToolbar/TableToolbar.tsx b/site/src/components/TableToolbar/TableToolbar.tsx index 9c5ef7708f278..ae249ba093f0e 100644 --- a/site/src/components/TableToolbar/TableToolbar.tsx +++ b/site/src/components/TableToolbar/TableToolbar.tsx @@ -5,7 +5,7 @@ import Skeleton from "@mui/material/Skeleton"; export const TableToolbar = styled(Box)(({ theme }) => ({ fontSize: 13, marginBottom: theme.spacing(1), - marginTop: theme.spacing(0), + marginTop: 0, height: 36, // The size of a small button color: theme.palette.text.secondary, "& strong": { color: theme.palette.text.primary }, diff --git a/site/src/components/UserAutocomplete/UserAutocomplete.tsx b/site/src/components/UserAutocomplete/UserAutocomplete.tsx index 44aee30bc5c89..ae6ade772c553 100644 --- a/site/src/components/UserAutocomplete/UserAutocomplete.tsx +++ b/site/src/components/UserAutocomplete/UserAutocomplete.tsx @@ -1,11 +1,17 @@ +import { css } from "@emotion/css"; +import { useTheme } from "@emotion/react"; import CircularProgress from "@mui/material/CircularProgress"; -import { makeStyles } from "@mui/styles"; import TextField from "@mui/material/TextField"; import Autocomplete from "@mui/material/Autocomplete"; -import { User } from "api/typesGenerated"; +import type { User } from "api/typesGenerated"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/AvatarData/AvatarData"; -import { ChangeEvent, ComponentProps, FC, useState } from "react"; +import { + type ChangeEvent, + type ComponentProps, + type FC, + useState, +} from "react"; import Box from "@mui/material/Box"; import { useDebouncedFunction } from "hooks/debounce"; import { useQuery } from "react-query"; @@ -27,7 +33,7 @@ export const UserAutocomplete: FC = ({ className, size = "small", }) => { - const styles = useStyles(); + const theme = useTheme(); const [autoComplete, setAutoComplete] = useState<{ value: string; open: boolean; @@ -101,7 +107,11 @@ export const UserAutocomplete: FC = ({ size={size} label={label} placeholder="User email or username" - className={styles.textField} + css={{ + "&:not(:has(label))": { + margin: 0, + }, + }} InputProps={{ ...params.InputProps, onChange: debouncedInputOnChange, @@ -119,7 +129,12 @@ export const UserAutocomplete: FC = ({ ), classes: { - root: styles.inputRoot, + root: css` + padding-left: ${theme.spacing( + 1.75, + )} !important; // Same padding left as input + gap: ${theme.spacing(0.5)}; + `, }, }} InputLabelProps={{ @@ -130,15 +145,3 @@ export const UserAutocomplete: FC = ({ /> ); }; - -export const useStyles = makeStyles((theme) => ({ - textField: { - "&:not(:has(label))": { - margin: 0, - }, - }, - inputRoot: { - paddingLeft: `${theme.spacing(1.75)} !important`, // Same padding left as input - gap: theme.spacing(0.5), - }, -})); diff --git a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx index e1fa8463f7602..c728af060e841 100644 --- a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx +++ b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx @@ -1,11 +1,10 @@ -import { makeStyles } from "@mui/styles"; import dayjs from "dayjs"; -import { ComponentProps, FC, Fragment } from "react"; -import { ProvisionerJobLog } from "api/typesGenerated"; +import { type ComponentProps, type FC, Fragment } from "react"; +import type { ProvisionerJobLog } from "api/typesGenerated"; import { MONOSPACE_FONT_FAMILY } from "theme/constants"; import { Logs } from "./Logs"; import Box from "@mui/material/Box"; -import { combineClasses } from "utils/combineClasses"; +import { type Interpolation, type Theme } from "@emotion/react"; const Language = { seconds: "seconds", @@ -53,7 +52,6 @@ export const WorkspaceBuildLogs: FC = ({ }) => { const groupedLogsByStage = groupLogsByStage(logs); const stages = Object.keys(groupedLogsByStage); - const styles = useStyles(); return ( = ({ return ( -
+
{stage}
{shouldDisplayDuration && ( -
+
{duration} {Language.seconds}
)} @@ -100,8 +93,8 @@ export const WorkspaceBuildLogs: FC = ({ ); }; -const useStyles = makeStyles((theme) => ({ - header: { +const styles = { + header: (theme) => ({ fontSize: 13, fontWeight: 600, padding: theme.spacing(0.5, 3), @@ -119,16 +112,16 @@ const useStyles = makeStyles((theme) => ({ "&:first-child": { borderRadius: "8px 8px 0 0", }, - }, + }), sticky: { position: "sticky", top: 0, }, - duration: { + duration: (theme) => ({ marginLeft: "auto", color: theme.palette.text.secondary, fontSize: 12, - }, -})); + }), +} satisfies Record>; diff --git a/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx b/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx index 17e8cafc7bebe..f01821eee2f53 100644 --- a/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx +++ b/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx @@ -1,15 +1,17 @@ -import { Workspace } from "api/typesGenerated"; +import type { Workspace } from "api/typesGenerated"; import { Pill } from "components/Pill/Pill"; -import { FC, PropsWithChildren } from "react"; -import { makeStyles } from "@mui/styles"; -import { combineClasses } from "utils/combineClasses"; +import { type FC, type PropsWithChildren } from "react"; import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; import { DormantDeletionText } from "components/WorkspaceDeletion"; import { getDisplayWorkspaceStatus } from "utils/workspace"; -import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip"; +import Tooltip, { + type TooltipProps, + tooltipClasses, +} from "@mui/material/Tooltip"; import { styled } from "@mui/material/styles"; import Box from "@mui/material/Box"; import ErrorOutline from "@mui/icons-material/ErrorOutline"; +import { type Interpolation, type Theme } from "@emotion/react"; export type WorkspaceStatusBadgeProps = { workspace: Workspace; @@ -56,7 +58,6 @@ export const WorkspaceStatusBadge: FC< export const WorkspaceStatusText: FC< PropsWithChildren > = ({ workspace, className }) => { - const styles = useStyles(); const { text, type } = getDisplayWorkspaceStatus( workspace.latest_build.status, ); @@ -71,11 +72,8 @@ export const WorkspaceStatusText: FC< {text} @@ -95,27 +93,22 @@ const FailureTooltip = styled(({ className, ...props }: TooltipProps) => ( }, })); -const useStyles = makeStyles((theme) => ({ +const styles = { root: { fontWeight: 600 }, - "type-error": { + + "type-error": (theme) => ({ color: theme.palette.error.light, - }, - "type-warning": { + }), + "type-warning": (theme) => ({ color: theme.palette.warning.light, - }, - "type-success": { + }), + "type-success": (theme) => ({ color: theme.palette.success.light, - }, - "type-info": { + }), + "type-info": (theme) => ({ color: theme.palette.info.light, - }, - "type-undefined": { + }), + "type-undefined": (theme) => ({ color: theme.palette.text.secondary, - }, - "type-primary": { - color: theme.palette.text.primary, - }, - "type-secondary": { - color: theme.palette.text.secondary, - }, -})); + }), +} satisfies Record>; diff --git a/site/src/hooks/useClickableTableRow.ts b/site/src/hooks/useClickableTableRow.ts index 673cf78028f31..2f220193f2bcb 100644 --- a/site/src/hooks/useClickableTableRow.ts +++ b/site/src/hooks/useClickableTableRow.ts @@ -1,11 +1,11 @@ +import { useTheme, type CSSObject } from "@emotion/react"; import { type MouseEventHandler } from "react"; import { type TableRowProps } from "@mui/material/TableRow"; -import { makeStyles } from "@mui/styles"; import { useClickable, type UseClickableResult } from "./useClickable"; type UseClickableTableRowResult = UseClickableResult & TableRowProps & { - className: string; + css: CSSObject; hover: true; onAuxClick: MouseEventHandler; }; @@ -28,12 +28,24 @@ export const useClickableTableRow = ({ onDoubleClick, onMiddleClick, }: UseClickableTableRowConfig): UseClickableTableRowResult => { - const styles = useStyles(); const clickableProps = useClickable(onClick); + const theme = useTheme(); return { ...clickableProps, - className: styles.row, + css: { + cursor: "pointer", + + "&:focus": { + outline: `1px solid ${theme.palette.secondary.dark}`, + outlineOffset: -1, + }, + + "&:last-of-type": { + borderBottomLeftRadius: theme.shape.borderRadius, + borderBottomRightRadius: theme.shape.borderRadius, + }, + }, hover: true, onDoubleClick, onAuxClick: (event) => { @@ -46,19 +58,3 @@ export const useClickableTableRow = ({ }, }; }; - -const useStyles = makeStyles((theme) => ({ - row: { - cursor: "pointer", - - "&:focus": { - outline: `1px solid ${theme.palette.secondary.dark}`, - outlineOffset: -1, - }, - - "&:last-of-type": { - borderBottomLeftRadius: theme.shape.borderRadius, - borderBottomRightRadius: theme.shape.borderRadius, - }, - }, -})); diff --git a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx index 2c7640e811d29..077cb46338578 100644 --- a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx +++ b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx @@ -1,7 +1,7 @@ +import { type Interpolation, type Theme } from "@emotion/react"; import Checkbox from "@mui/material/Checkbox"; -import { makeStyles } from "@mui/styles"; import TextField from "@mui/material/TextField"; -import { +import type { ProvisionerJobLog, Template, TemplateExample, @@ -9,10 +9,10 @@ import { VariableValue, } from "api/typesGenerated"; import { Stack } from "components/Stack/Stack"; -import { TemplateUpload, TemplateUploadProps } from "./TemplateUpload"; +import { TemplateUpload, type TemplateUploadProps } from "./TemplateUpload"; import { useFormik } from "formik"; import { SelectedTemplate } from "pages/CreateWorkspacePage/SelectedTemplate"; -import { FC, useEffect } from "react"; +import { type FC, useEffect } from "react"; import { nameValidator, getFormHelpers, @@ -43,8 +43,8 @@ import { } from "pages/TemplateSettingsPage/TemplateSchedulePage/AutostopRequirementHelperText"; import MenuItem from "@mui/material/MenuItem"; import { - TemplateAutostartRequirementDaysValue, - TemplateAutostopRequirementDaysValue, + type TemplateAutostartRequirementDaysValue, + type TemplateAutostopRequirementDaysValue, } from "utils/schedule"; import { TemplateScheduleAutostart, @@ -218,7 +218,6 @@ export const CreateTemplateForm: FC = (props) => { allowDisableEveryoneAccess, allowAutostopRequirement, } = props; - const styles = useStyles(); const form = useFormik({ initialValues: getInitialValues({ allowAdvancedScheduling, @@ -338,7 +337,7 @@ export const CreateTemplateForm: FC = (props) => { description="Define when workspaces created from this template automatically stop." > - + = (props) => { {allowAutostopRequirement && ( - + = (props) => { Allow users to customize autostop duration for workspaces. - + Workspaces will always use the default TTL if this is set. Regardless of this setting, workspaces can only stay on for the max TTL. @@ -514,7 +513,7 @@ export const CreateTemplateForm: FC = (props) => { direction="row" alignItems="center" spacing={0.5} - className={styles.optionText} + css={styles.optionText} > Allow users to cancel in-progress workspace jobs @@ -527,7 +526,7 @@ export const CreateTemplateForm: FC = (props) => { - + Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases. @@ -552,7 +551,7 @@ export const CreateTemplateForm: FC = (props) => { direction="row" alignItems="center" spacing={0.5} - className={styles.optionText} + css={styles.optionText} > Allow everyone to use the template @@ -569,7 +568,7 @@ export const CreateTemplateForm: FC = (props) => { - + This setting requires an enterprise license for the  'Template RBAC' @@ -610,14 +609,14 @@ export const CreateTemplateForm: FC = (props) => { {jobError && ( -
-
Error during provisioning
-

+

+
Error during provisioning
+

Looks like we found an error during the template provisioning. You can see the logs bellow.

- {jobError} + {jobError}
@@ -690,43 +689,43 @@ const MaxTTLHelperText = (props: { ttl?: number }) => { ); }; -const useStyles = makeStyles((theme) => ({ +const styles = { ttlFields: { width: "100%", }, - optionText: { + optionText: (theme) => ({ fontSize: theme.spacing(2), color: theme.palette.text.primary, - }, + }), - optionHelperText: { + optionHelperText: (theme) => ({ fontSize: theme.spacing(1.5), color: theme.palette.text.secondary, - }, + }), - error: { + error: (theme) => ({ padding: theme.spacing(3), borderRadius: theme.spacing(1), background: theme.palette.background.paper, border: `1px solid ${theme.palette.error.main}`, - }, + }), errorTitle: { fontSize: 16, margin: 0, }, - errorDescription: { + errorDescription: (theme) => ({ margin: 0, color: theme.palette.text.secondary, marginTop: theme.spacing(0.5), - }, + }), - errorDetails: { + errorDetails: (theme) => ({ display: "block", marginTop: theme.spacing(1), color: theme.palette.error.light, fontSize: theme.spacing(2), - }, -})); + }), +} satisfies Record>; diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index a4afc25bfc4c2..edcee19798cfb 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -1,8 +1,10 @@ +import { css } from "@emotion/css"; +import { useTheme, type Interpolation, type Theme } from "@emotion/react"; import TextField from "@mui/material/TextField"; -import * as TypesGen from "api/typesGenerated"; +import type * as TypesGen from "api/typesGenerated"; import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"; import { FormikContextType, useFormik } from "formik"; -import { FC, useEffect, useState } from "react"; +import { type FC, useEffect, useState } from "react"; import { getFormHelpers, nameValidator, @@ -17,7 +19,6 @@ import { FormFooter, HorizontalForm, } from "components/Form/Form"; -import { makeStyles } from "@mui/styles"; import { getInitialRichParameterValues, useValidationSchemaForRichParameters, @@ -31,7 +32,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Stack } from "components/Stack/Stack"; import { type ExternalAuthPollingState } from "./CreateWorkspacePage"; import { useSearchParams } from "react-router-dom"; -import { CreateWSPermissions } from "./permissions"; +import type { CreateWSPermissions } from "./permissions"; export interface CreateWorkspacePageViewProps { error: unknown; @@ -69,7 +70,7 @@ export const CreateWorkspacePageView: FC = ({ onSubmit, onCancel, }) => { - const styles = useStyles(); + const theme = useTheme(); const [owner, setOwner] = useState(defaultOwner); const { verifyExternalAuth, externalAuthErrors } = useExternalAuthVerification(externalAuth); @@ -123,14 +124,14 @@ export const CreateWorkspacePageView: FC = ({ {versionId && versionId !== template.active_version_id && ( - + - + This parameter has been preset, and cannot be modified. @@ -210,7 +211,16 @@ export const CreateWorkspacePageView: FC = ({ /> { return { ...getFieldHelpers( @@ -279,23 +289,12 @@ const useExternalAuthVerification = ( }; }; -const useStyles = makeStyles((theme) => ({ - hasDescription: { +const styles = { + hasDescription: (theme) => ({ paddingBottom: theme.spacing(2), - }, - description: { + }), + description: (theme) => ({ fontSize: 13, color: theme.palette.text.secondary, - }, - warningText: { - color: theme.palette.warning.light, - }, - warningSection: { - border: `1px solid ${theme.palette.warning.light}`, - borderRadius: 8, - backgroundColor: theme.palette.background.paper, - padding: theme.spacing(10), - marginLeft: theme.spacing(-10), - marginRight: theme.spacing(-10), - }, -})); + }), +} satisfies Record>; diff --git a/site/src/pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx b/site/src/pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx index 12d47d738ef5c..ede23c64e317e 100644 --- a/site/src/pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx +++ b/site/src/pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPageView.tsx @@ -1,13 +1,14 @@ +import { type Interpolation, type Theme } from "@emotion/react"; import Button from "@mui/material/Button"; -import { makeStyles, useTheme } from "@mui/styles"; +import { useTheme } from "@mui/styles"; import Skeleton from "@mui/material/Skeleton"; import AddIcon from "@mui/icons-material/AddOutlined"; import RefreshIcon from "@mui/icons-material/Refresh"; -import { GetLicensesResponse } from "api/api"; +import type { GetLicensesResponse } from "api/api"; import { Header } from "components/DeploySettingsLayout/Header"; import { LicenseCard } from "./LicenseCard"; import { Stack } from "components/Stack/Stack"; -import { FC } from "react"; +import { type FC } from "react"; import Confetti from "react-confetti"; import { Link } from "react-router-dom"; import useWindowSize from "react-use/lib/useWindowSize"; @@ -38,10 +39,8 @@ const LicensesSettingsPageView: FC = ({ removeLicense, refreshEntitlements, }) => { - const styles = useStyles(); - const { width, height } = useWindowSize(); - const theme = useTheme(); + const { width, height } = useWindowSize(); return ( <> @@ -107,13 +106,11 @@ const LicensesSettingsPageView: FC = ({ )} {!isLoading && licenses === null && ( -
+
- - You don't have any licenses! - - + You don't have any licenses! + You're missing out on high availability, RBAC, quotas, and much more. Contact{" "} sales or{" "} @@ -130,12 +127,12 @@ const LicensesSettingsPageView: FC = ({ ); }; -const useStyles = makeStyles((theme) => ({ - title: { +const styles = { + title: (theme) => ({ fontSize: theme.spacing(2), - }, + }), - root: { + root: (theme) => ({ minHeight: theme.spacing(30), display: "flex", alignItems: "center", @@ -143,14 +140,14 @@ const useStyles = makeStyles((theme) => ({ borderRadius: theme.shape.borderRadius, border: `1px solid ${theme.palette.divider}`, padding: theme.spacing(6), - }, + }), - description: { + description: (theme) => ({ color: theme.palette.text.secondary, textAlign: "center", maxWidth: theme.spacing(58), marginTop: theme.spacing(1), - }, -})); + }), +} satisfies Record>; export default LicensesSettingsPageView; diff --git a/site/src/pages/GroupsPage/GroupsPageView.tsx b/site/src/pages/GroupsPage/GroupsPageView.tsx index 8fe70c9c5ee26..b454dcc14b2b1 100644 --- a/site/src/pages/GroupsPage/GroupsPageView.tsx +++ b/site/src/pages/GroupsPage/GroupsPageView.tsx @@ -1,6 +1,6 @@ +import { type Interpolation, type Theme } from "@emotion/react"; import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; -import { makeStyles } from "@mui/styles"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; @@ -20,10 +20,10 @@ import { TableRowSkeleton, } from "components/TableLoader/TableLoader"; import { UserAvatar } from "components/UserAvatar/UserAvatar"; -import { FC } from "react"; +import { type FC } from "react"; import { Link as RouterLink, useNavigate } from "react-router-dom"; import { Paywall } from "components/Paywall/Paywall"; -import { Group } from "api/typesGenerated"; +import type { Group } from "api/typesGenerated"; import { GroupAvatar } from "components/GroupAvatar/GroupAvatar"; import { docs } from "utils/docs"; import Skeleton from "@mui/material/Skeleton"; @@ -44,7 +44,6 @@ export const GroupsPageView: FC = ({ const isLoading = Boolean(groups === undefined); const isEmpty = Boolean(groups && groups.length === 0); const navigate = useNavigate(); - const styles = useStyles(); return ( <> @@ -137,7 +136,7 @@ export const GroupsPageView: FC = ({ navigate(groupPageLink); } }} - className={styles.clickableTableRow} + css={styles.clickableTableRow} > = ({ -
- +
+
@@ -210,8 +207,8 @@ const TableLoader = () => { ); }; -const useStyles = makeStyles((theme) => ({ - clickableTableRow: { +const styles = { + clickableTableRow: (theme) => ({ cursor: "pointer", "&:hover td": { @@ -223,17 +220,17 @@ const useStyles = makeStyles((theme) => ({ }, "& .MuiTableCell-root:last-child": { - paddingRight: theme.spacing(2), + paddingRight: `${theme.spacing(2)} !important`, }, - }, - arrowRight: { + }), + arrowRight: (theme) => ({ color: theme.palette.text.secondary, width: 20, height: 20, - }, + }), arrowCell: { display: "flex", }, -})); +} satisfies Record>; export default GroupsPageView; diff --git a/site/src/pages/LoginPage/LoginPageView.stories.tsx b/site/src/pages/LoginPage/LoginPageView.stories.tsx index 13d1734507536..503444f49ee0b 100644 --- a/site/src/pages/LoginPage/LoginPageView.stories.tsx +++ b/site/src/pages/LoginPage/LoginPageView.stories.tsx @@ -1,4 +1,9 @@ -import { MockAuthMethods, mockApiError } from "testHelpers/entities"; +import { + MockAuthMethodsAll, + MockAuthMethodsExternal, + MockAuthMethodsPasswordOnly, + mockApiError, +} from "testHelpers/entities"; import { LoginPageView } from "./LoginPageView"; import type { Meta, StoryObj } from "@storybook/react"; @@ -12,17 +17,37 @@ type Story = StoryObj; export const Example: Story = { args: { - authMethods: MockAuthMethods, + authMethods: MockAuthMethodsPasswordOnly, + }, +}; + +export const WithExternalAuthMethods: Story = { + args: { + authMethods: MockAuthMethodsExternal, + }, +}; + +export const WithAllAuthMethods: Story = { + args: { + authMethods: MockAuthMethodsAll, }, }; export const AuthError: Story = { args: { error: mockApiError({ - message: "User or password is incorrect", - detail: "Please, try again", + message: "Incorrect email or password.", + }), + authMethods: MockAuthMethodsPasswordOnly, + }, +}; + +export const ExternalAuthError: Story = { + args: { + error: mockApiError({ + message: "Incorrect email or password.", }), - authMethods: MockAuthMethods, + authMethods: MockAuthMethodsAll, }, }; @@ -35,6 +60,6 @@ export const LoadingAuthMethods: Story = { export const SigningIn: Story = { args: { isSigningIn: true, - authMethods: MockAuthMethods, + authMethods: MockAuthMethodsPasswordOnly, }, }; diff --git a/site/src/pages/StarterTemplatePage/StarterTemplatePageView.tsx b/site/src/pages/StarterTemplatePage/StarterTemplatePageView.tsx index 0264ce0eef4b4..a37d0ca918714 100644 --- a/site/src/pages/StarterTemplatePage/StarterTemplatePageView.tsx +++ b/site/src/pages/StarterTemplatePage/StarterTemplatePageView.tsx @@ -1,5 +1,5 @@ import Button from "@mui/material/Button"; -import { makeStyles } from "@mui/styles"; +import { useTheme } from "@emotion/react"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { MemoizedMarkdown } from "components/Markdown/Markdown"; @@ -8,13 +8,13 @@ import { PageHeaderSubtitle, PageHeaderTitle, } from "components/PageHeader/PageHeader"; -import { FC } from "react"; +import { type FC } from "react"; import ViewCodeIcon from "@mui/icons-material/OpenInNewOutlined"; import PlusIcon from "@mui/icons-material/AddOutlined"; import { Stack } from "components/Stack/Stack"; import { Link } from "react-router-dom"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { TemplateExample } from "api/typesGenerated"; +import type { TemplateExample } from "api/typesGenerated"; export interface StarterTemplatePageViewProps { starterTemplate?: TemplateExample; @@ -25,7 +25,7 @@ export const StarterTemplatePageView: FC = ({ starterTemplate, error, }) => { - const styles = useStyles(); + const theme = useTheme(); if (error) { return ( @@ -65,7 +65,19 @@ export const StarterTemplatePageView: FC = ({ } > -
+
@@ -77,39 +89,24 @@ export const StarterTemplatePageView: FC = ({ -
-
+
+
{starterTemplate.markdown}
); }; - -export const useStyles = makeStyles((theme) => { - return { - icon: { - height: theme.spacing(6), - width: theme.spacing(6), - display: "flex", - alignItems: "center", - justifyContent: "center", - - "& img": { - width: "100%", - }, - }, - - markdownSection: { - background: theme.palette.background.paper, - border: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadius, - }, - - markdownWrapper: { - padding: theme.spacing(5, 5, 8), - maxWidth: 800, - margin: "auto", - }, - }; -}); diff --git a/site/src/pages/StarterTemplatesPage/StarterTemplatesPageView.tsx b/site/src/pages/StarterTemplatesPage/StarterTemplatesPageView.tsx index 5a7e5e1b830b3..5b874d3a41b10 100644 --- a/site/src/pages/StarterTemplatesPage/StarterTemplatesPageView.tsx +++ b/site/src/pages/StarterTemplatesPage/StarterTemplatesPageView.tsx @@ -1,4 +1,4 @@ -import { makeStyles } from "@mui/styles"; +import { type Interpolation, type Theme } from "@emotion/react"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; @@ -9,10 +9,9 @@ import { } from "components/PageHeader/PageHeader"; import { Stack } from "components/Stack/Stack"; import { TemplateExampleCard } from "components/TemplateExampleCard/TemplateExampleCard"; -import { FC } from "react"; +import { type FC } from "react"; import { Link, useSearchParams } from "react-router-dom"; -import { combineClasses } from "utils/combineClasses"; -import { StarterTemplatesByTag } from "utils/starterTemplates"; +import type { StarterTemplatesByTag } from "utils/starterTemplates"; const getTagLabel = (tag: string) => { const labelByTag: Record = { @@ -40,7 +39,6 @@ export const StarterTemplatesPageView: FC = ({ error, }) => { const [urlParams] = useSearchParams(); - const styles = useStyles(); const tags = starterTemplatesByTag ? selectTags(starterTemplatesByTag) : undefined; @@ -64,16 +62,16 @@ export const StarterTemplatesPageView: FC = ({ {starterTemplatesByTag && tags && ( - - Filter + + Filter {tags.map((tag) => ( {getTagLabel(tag)} ({starterTemplatesByTag[tag].length}) @@ -81,7 +79,7 @@ export const StarterTemplatesPageView: FC = ({ )} -
+
{visibleTemplates && visibleTemplates.map((example) => ( @@ -92,21 +90,21 @@ export const StarterTemplatesPageView: FC = ({ ); }; -const useStyles = makeStyles((theme) => ({ - filter: { +const styles = { + filter: (theme) => ({ width: theme.spacing(26), flexShrink: 0, - }, + }), - filterCaption: { + filterCaption: (theme) => ({ textTransform: "uppercase", fontWeight: 600, fontSize: 12, color: theme.palette.text.secondary, letterSpacing: "0.1em", - }, + }), - tagLink: { + tagLink: (theme) => ({ color: theme.palette.text.secondary, textDecoration: "none", fontSize: 14, @@ -115,18 +113,18 @@ const useStyles = makeStyles((theme) => ({ "&:hover": { color: theme.palette.text.primary, }, - }, + }), - tagLinkActive: { + tagLinkActive: (theme) => ({ color: theme.palette.text.primary, fontWeight: 600, - }, + }), - templates: { + templates: (theme) => ({ flex: "1", display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", gap: theme.spacing(2), gridAutoRows: "min-content", - }, -})); + }), +} satisfies Record>; diff --git a/site/src/pages/TemplateSettingsPage/Sidebar.tsx b/site/src/pages/TemplateSettingsPage/Sidebar.tsx index 1947942241f8b..fe10d385b6f80 100644 --- a/site/src/pages/TemplateSettingsPage/Sidebar.tsx +++ b/site/src/pages/TemplateSettingsPage/Sidebar.tsx @@ -1,27 +1,72 @@ -import { makeStyles } from "@mui/styles"; +import { css } from "@emotion/css"; +import { + useTheme, + type CSSObject, + type Interpolation, + type Theme, +} from "@emotion/react"; import ScheduleIcon from "@mui/icons-material/TimerOutlined"; import VariablesIcon from "@mui/icons-material/CodeOutlined"; -import { Template } from "api/typesGenerated"; +import type { Template } from "api/typesGenerated"; import { Stack } from "components/Stack/Stack"; -import { FC, ElementType, PropsWithChildren, ReactNode } from "react"; +import { + type FC, + type ElementType, + type PropsWithChildren, + type ReactNode, +} from "react"; import { Link, NavLink } from "react-router-dom"; -import { combineClasses } from "utils/combineClasses"; import GeneralIcon from "@mui/icons-material/SettingsOutlined"; import SecurityIcon from "@mui/icons-material/LockOutlined"; import { Avatar } from "components/Avatar/Avatar"; +import { combineClasses } from "utils/combineClasses"; const SidebarNavItem: FC< PropsWithChildren<{ href: string; icon: ReactNode }> > = ({ children, href, icon }) => { - const styles = useStyles(); + const theme = useTheme(); + + const sidebarNavItemStyles = css` + color: inherit; + display: block; + font-size: 14px; + text-decoration: none; + padding: ${theme.spacing(1.5, 1.5, 1.5, 2)}; + border-radius: ${theme.shape.borderRadius / 2}px; + transition: background-color 0.15s ease-in-out; + margin-bottom: 1px; + position: relative; + + &:hover { + background-color: ${theme.palette.action.hover}; + } + `; + + const sidebarNavItemActiveStyles = css` + background-color: ${theme.palette.action.hover}; + + &:before { + content: ""; + display: block; + width: 3px; + height: 100%; + position: absolute; + left: 0; + top: 0; + background-color: ${theme.palette.secondary.dark}; + border-top-left-radius: ${theme.shape.borderRadius}px; + border-bottom-left-radius: ${theme.shape.borderRadius}px; + } + `; + return ( combineClasses([ - styles.sidebarNavItem, - isActive ? styles.sidebarNavItemActive : undefined, + sidebarNavItemStyles, + isActive ? sidebarNavItemActiveStyles : undefined, ]) } > @@ -36,28 +81,21 @@ const SidebarNavItem: FC< const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({ icon: Icon, }) => { - const styles = useStyles(); - return ; + return ; }; export const Sidebar: React.FC<{ template: Template }> = ({ template }) => { - const styles = useStyles(); - return ( -