From 320cd40d7c3701d2027b5d84708d8720d5f8036a Mon Sep 17 00:00:00 2001 From: Jake Howell Date: Tue, 4 Aug 2026 04:12:31 +0000 Subject: [PATCH 1/4] refactor(site): replace MUI Stack, TextField, and Link with shared components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continue the MUI → Tailwind/shadcn migration across a few auth and settings surfaces. - Create token form: MUI `TextField`/`MenuItem` → `FormField`, `Select`, and `Input` (drops Emotion for section min-width) - Create organization form: `TextField` → `FormField` / `Textarea`, matching the organization info form - SSO security section and external auth: MUI `Link`/`TextField` → shared `Link`, `Input`, and `Label` - Permission and IdP pill lists: MUI `Stack` → `flex flex-row gap-2` - Minor link layout polish on Git device auth / external auth pages --- .../GitDeviceAuth/GitDeviceAuth.tsx | 4 +- .../pages/CreateTokenPage/CreateTokenForm.tsx | 135 ++++++++++-------- .../ExternalAuthPage/ExternalAuthPageView.tsx | 14 +- .../CreateOrganizationPageView.tsx | 67 +++++++-- .../CustomRolesPage/PermissionPillsList.tsx | 5 +- .../IdpSyncPage/IdpPillList.tsx | 5 +- .../SecurityPage/SingleSignOnSection.tsx | 55 ++++--- 7 files changed, 176 insertions(+), 109 deletions(-) diff --git a/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx b/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx index 6f540448d0a..e1e5bd2035a 100644 --- a/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx +++ b/site/src/components/GitDeviceAuth/GitDeviceAuth.tsx @@ -146,9 +146,9 @@ export const GitDeviceAuth: FC = ({
Then open the link below and paste it:

-
+
= ({ now, }) => { const navigate = useNavigate(); + const lifetimeId = useId(); + const expiresOnId = useId(); const [expDays, setExpDays] = useState(1); const [lifetimeDays, setLifetimeDays] = useState( @@ -68,16 +77,16 @@ export const CreateTokenForm: FC = ({ - setFormError(undefined))} autoFocus - fullWidth + className="w-full" /> @@ -98,59 +107,69 @@ export const CreateTokenForm: FC = ({ "Please set a token expiration." ) } - classes={{ sectionInfo: classNames.sectionInfo }} + classes={{ sectionInfo: "min-w-[300px]" }} >
- { - void setLifetimeDays(event.target.value); - }} - fullWidth - > - {filterByMaxTokenLifetime(maxTokenLifetime).map((lt) => ( - - {lt.label} - - ))} - + + +
{lifetimeDays === "custom" && ( - { - const lt = Math.ceil( - dayjs(event.target.value).diff(dayjs(), "day", true), - ); - setExpDays(lt); - }} - inputProps={{ - "data-pixel": "ignore", - min: dayjs().add(1, "day").format("YYYY-MM-DD"), - max: maxTokenLifetime - ? dayjs() - .add(maxTokenLifetime / NANO_HOUR / 24, "day") - .format("YYYY-MM-DD") - : undefined, - required: true, - }} - fullWidth - InputLabelProps={{ - required: true, - }} - /> +
+ + { + const lt = Math.ceil( + dayjs(event.target.value).diff(dayjs(), "day", true), + ); + setExpDays(lt); + }} + /> +
)}
@@ -168,9 +187,3 @@ export const CreateTokenForm: FC = ({ ); }; - -const classNames = { - sectionInfo: css` - min-width: 300px; - `, -}; diff --git a/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx b/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx index bace48e3264..697983ce985 100644 --- a/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx +++ b/site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx @@ -1,4 +1,3 @@ -import Link from "@mui/material/Link"; import { ExternalLinkIcon, RotateCwIcon } from "lucide-react"; import type { FC, ReactNode } from "react"; import type { ApiErrorResponse } from "#/api/errors"; @@ -6,6 +5,7 @@ import type { ExternalAuth, ExternalAuthDevice } from "#/api/typesGenerated"; import { Alert } from "#/components/Alert/Alert"; import { Avatar } from "#/components/Avatar/Avatar"; import { GitDeviceAuth } from "#/components/GitDeviceAuth/GitDeviceAuth"; +import { Link } from "#/components/Link/Link"; import { SignInLayout } from "#/components/SignInLayout/SignInLayout"; import { Tooltip, @@ -56,6 +56,8 @@ const ExternalAuthPageView: FC = ({ href={externalAuth.app_install_url} target="_blank" rel="noreferrer" + showExternalIcon={false} + className="p-0 text-sm font-medium" > {installTheApp} @@ -88,6 +90,8 @@ const ExternalAuthPageView: FC = ({ href={install.account.profile_url} target="_blank" rel="noreferrer" + showExternalIcon={false} + className="p-0 after:hover:content-none" > = ({
)} -
+
{!hasInstallations && externalAuth.app_installable && ( You must {installTheApp} to clone private repositories. Accounts @@ -122,7 +126,8 @@ const ExternalAuthPageView: FC = ({ href={externalAuth.app_install_url} target="_blank" rel="noreferrer" - className="flex items-center justify-center gap-2 text-base" + showExternalIcon={false} + className="inline-flex items-center gap-2 p-0 text-base font-medium [&_svg]:size-icon-xs [&_svg]:p-0" > {externalAuth.installations.length > 0 ? "Configure" : "Install"}{" "} @@ -130,8 +135,9 @@ const ExternalAuthPageView: FC = ({ )} { onReauthenticate(); }} diff --git a/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx b/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx index 4b1bfedf219..69980a65455 100644 --- a/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx +++ b/site/src/pages/OrganizationSettingsPage/CreateOrganizationPageView.tsx @@ -1,4 +1,3 @@ -import TextField from "@mui/material/TextField"; import { useFormik } from "formik"; import { ArrowLeftIcon } from "lucide-react"; import type { FC } from "react"; @@ -9,15 +8,19 @@ import type { CreateOrganizationRequest } from "#/api/typesGenerated"; import { ErrorAlert } from "#/components/Alert/ErrorAlert"; import { Badges, PremiumBadge } from "#/components/Badges/Badges"; import { Button } from "#/components/Button/Button"; +import { FormField } from "#/components/FormField/FormField"; import { IconField } from "#/components/IconField/IconField"; +import { Label } from "#/components/Label/Label"; import { PaywallPremium } from "#/components/Paywall/PaywallPremium"; import { PopoverPaywall } from "#/components/Paywall/PopoverPaywall"; import { Spinner } from "#/components/Spinner/Spinner"; +import { Textarea } from "#/components/Textarea/Textarea"; import { Tooltip, TooltipContent, TooltipTrigger, } from "#/components/Tooltip/Tooltip"; +import { cn } from "#/utils/cn"; import { docs } from "#/utils/docs"; import { displayNameValidator, @@ -59,6 +62,11 @@ export const CreateOrganizationPageView: FC< }); const navigate = useNavigate(); const getFieldHelpers = getFormHelpers(form, error); + const descriptionField = getFieldHelpers("description", { + maxLength: MAX_DESCRIPTION_CHAR_LIMIT, + }); + const descriptionErrorId = `${descriptionField.id}-error`; + const descriptionHelperId = `${descriptionField.id}-helper`; return (
@@ -130,23 +138,54 @@ export const CreateOrganizationPageView: FC< disabled={form.isSubmitting} className="flex flex-col gap-6 w-full border-none" > - - - +
+ +