chore: remove circular dependencies (#17585)

I've been bit in the past by hard to deduce bugs caused by circular
dependencies within TS projects. On a hunch that this could be
contributing to some flaky tests I've used the tool
[dpdm](https://github.com/acrazing/dpdm) to find and remove them.

This PR does the following:
- Move around exports/create new files to remove any non-type circular
depencies
- Add dpdm as a dev dependency and create the `check:circular-depency`
pnpm script
This commit is contained in:
brettkolodny
2025-04-28 16:51:58 -04:00
committed by GitHub
parent 12589026b6
commit b6146dfe8a
42 changed files with 180 additions and 75 deletions
+3 -1
View File
@@ -13,8 +13,9 @@
"dev": "vite",
"format": "biome format --write .",
"format:check": "biome format .",
"lint": "pnpm run lint:check && pnpm run lint:types",
"lint": "pnpm run lint:check && pnpm run lint:types && pnpm run lint:circular-deps",
"lint:check": " biome lint --error-on-warnings .",
"lint:circular-deps": "dpdm --no-tree --no-warning -T ./src/App.tsx",
"lint:fix": " biome lint --error-on-warnings --write .",
"lint:types": "tsc -p .",
"playwright:install": "playwright install --with-deps chromium",
@@ -171,6 +172,7 @@
"@vitejs/plugin-react": "4.3.4",
"autoprefixer": "10.4.20",
"chromatic": "11.25.2",
"dpdm": "3.14.0",
"express": "4.21.2",
"jest": "29.7.0",
"jest-canvas-mock": "2.5.2",
+97
View File
@@ -422,6 +422,9 @@ importers:
chromatic:
specifier: 11.25.2
version: 11.25.2
dpdm:
specifier: 3.14.0
version: 3.14.0
express:
specifier: 4.21.2
version: 4.21.2
@@ -3223,6 +3226,14 @@ packages:
classnames@2.3.2:
resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==, tarball: https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz}
cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==, tarball: https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz}
engines: {node: '>=8'}
cli-spinners@2.9.2:
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==, tarball: https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz}
engines: {node: '>=6'}
cli-width@4.1.0:
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==, tarball: https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz}
engines: {node: '>= 12'}
@@ -3231,6 +3242,10 @@ packages:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, tarball: https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz}
engines: {node: '>=12'}
clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==, tarball: https://registry.npmjs.org/clone/-/clone-1.0.4.tgz}
engines: {node: '>=0.8'}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, tarball: https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz}
engines: {node: '>=6'}
@@ -3491,6 +3506,9 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, tarball: https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz}
engines: {node: '>=0.10.0'}
defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==, tarball: https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz}
define-data-property@1.1.1:
resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==, tarball: https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz}
engines: {node: '>= 0.4'}
@@ -3574,6 +3592,10 @@ packages:
engines: {node: '>=12'}
deprecated: Use your platform's native DOMException instead
dpdm@3.14.0:
resolution: {integrity: sha512-YJzsFSyEtj88q5eTELg3UWU7TVZkG1dpbF4JDQ3t1b07xuzXmdoGeSz9TKOke1mUuOpWlk4q+pBh+aHzD6GBTg==, tarball: https://registry.npmjs.org/dpdm/-/dpdm-3.14.0.tgz}
hasBin: true
dprint-node@1.0.8:
resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==, tarball: https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz}
@@ -4206,6 +4228,10 @@ packages:
is-hexadecimal@2.0.1:
resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==, tarball: https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz}
is-interactive@1.0.0:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==, tarball: https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz}
engines: {node: '>=8'}
is-map@2.0.2:
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==, tarball: https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz}
@@ -4261,6 +4287,10 @@ packages:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==, tarball: https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz}
engines: {node: '>= 0.4'}
is-unicode-supported@0.1.0:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==, tarball: https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz}
engines: {node: '>=10'}
is-weakmap@2.0.1:
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==, tarball: https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz}
@@ -4598,6 +4628,10 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, tarball: https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz}
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==, tarball: https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz}
engines: {node: '>=10'}
long@5.2.3:
resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==, tarball: https://registry.npmjs.org/long/-/long-5.2.3.tgz}
@@ -5062,6 +5096,10 @@ packages:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==, tarball: https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz}
engines: {node: '>= 0.8.0'}
ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==, tarball: https://registry.npmjs.org/ora/-/ora-5.4.1.tgz}
engines: {node: '>=10'}
outvariant@1.4.3:
resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==, tarball: https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz}
@@ -5606,6 +5644,10 @@ packages:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==, tarball: https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz}
hasBin: true
restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, tarball: https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz}
engines: {node: '>=8'}
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==, tarball: https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -6345,6 +6387,9 @@ packages:
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, tarball: https://registry.npmjs.org/walker/-/walker-1.0.8.tgz}
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==, tarball: https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz}
webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, tarball: https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz}
engines: {node: '>=12'}
@@ -9422,6 +9467,12 @@ snapshots:
classnames@2.3.2: {}
cli-cursor@3.1.0:
dependencies:
restore-cursor: 3.1.0
cli-spinners@2.9.2: {}
cli-width@4.1.0: {}
cliui@8.0.1:
@@ -9430,6 +9481,8 @@ snapshots:
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
clone@1.0.4: {}
clsx@2.1.1: {}
cmdk@1.0.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
@@ -9667,6 +9720,10 @@ snapshots:
deepmerge@4.3.1: {}
defaults@1.0.4:
dependencies:
clone: 1.0.4
define-data-property@1.1.1:
dependencies:
get-intrinsic: 1.3.0
@@ -9732,6 +9789,16 @@ snapshots:
dependencies:
webidl-conversions: 7.0.0
dpdm@3.14.0:
dependencies:
chalk: 4.1.2
fs-extra: 11.2.0
glob: 10.4.5
ora: 5.4.1
tslib: 2.8.1
typescript: 5.6.3
yargs: 17.7.2
dprint-node@1.0.8:
dependencies:
detect-libc: 1.0.3
@@ -10473,6 +10540,8 @@ snapshots:
is-hexadecimal@2.0.1: {}
is-interactive@1.0.0: {}
is-map@2.0.2: {}
is-node-process@1.2.0: {}
@@ -10522,6 +10591,8 @@ snapshots:
dependencies:
which-typed-array: 1.1.18
is-unicode-supported@0.1.0: {}
is-weakmap@2.0.1: {}
is-weakset@2.0.2:
@@ -11096,6 +11167,11 @@ snapshots:
lodash@4.17.21: {}
log-symbols@4.1.0:
dependencies:
chalk: 4.1.2
is-unicode-supported: 0.1.0
long@5.2.3: {}
longest-streak@3.1.0: {}
@@ -11829,6 +11905,18 @@ snapshots:
type-check: 0.4.0
optional: true
ora@5.4.1:
dependencies:
bl: 4.1.0
chalk: 4.1.2
cli-cursor: 3.1.0
cli-spinners: 2.9.2
is-interactive: 1.0.0
is-unicode-supported: 0.1.0
log-symbols: 4.1.0
strip-ansi: 6.0.1
wcwidth: 1.0.1
outvariant@1.4.3: {}
p-limit@2.3.0:
@@ -12441,6 +12529,11 @@ snapshots:
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
restore-cursor@3.1.0:
dependencies:
onetime: 5.1.2
signal-exit: 3.0.7
reusify@1.0.4: {}
rimraf@3.0.2:
@@ -13233,6 +13326,10 @@ snapshots:
dependencies:
makeerror: 1.0.12
wcwidth@1.0.1:
dependencies:
defaults: 1.0.4
webidl-conversions@7.0.0: {}
webpack-sources@3.2.3: {}
+1 -1
View File
@@ -5,7 +5,7 @@ import {
type SelectFilterOption,
SelectFilterSearch,
} from "components/Filter/SelectFilter";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { FC } from "react";
import { type UseFilterMenuOptions, useFilterMenu } from "./menu";
+1 -1
View File
@@ -1,7 +1,7 @@
import { API } from "api/api";
import { cachedQuery } from "api/queries/util";
import type { Region, WorkspaceProxy } from "api/typesGenerated";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import {
type FC,
+1 -1
View File
@@ -1,4 +1,5 @@
import { renderHook, screen } from "@testing-library/react";
import { useAuthenticated } from "hooks";
import { http, HttpResponse } from "msw";
import type { FC, PropsWithChildren } from "react";
import { QueryClientProvider } from "react-query";
@@ -9,7 +10,6 @@ import {
} from "testHelpers/renderHelpers";
import { server } from "testHelpers/server";
import { AuthContext, type AuthContextValue } from "./AuthProvider";
import { useAuthenticated } from "./RequireAuth";
describe("RequireAuth", () => {
it("redirects to /login if user is not authenticated", async () => {
+1 -26
View File
@@ -6,7 +6,7 @@ import { DashboardProvider as ProductionDashboardProvider } from "modules/dashbo
import { type FC, useEffect } from "react";
import { Navigate, Outlet, useLocation } from "react-router-dom";
import { embedRedirect } from "utils/redirect";
import { type AuthContextValue, useAuthContext } from "./AuthProvider";
import { useAuthContext } from "./AuthProvider";
type RequireAuthProps = Readonly<{
ProxyProvider?: typeof ProductionProxyProvider;
@@ -81,28 +81,3 @@ export const RequireAuth: FC<RequireAuthProps> = ({
</DashboardProvider>
);
};
type RequireKeys<T, R extends keyof T> = Omit<T, R> & {
[K in keyof Pick<T, R>]-?: NonNullable<T[K]>;
};
// We can do some TS magic here but I would rather to be explicit on what
// values are not undefined when authenticated
type AuthenticatedAuthContextValue = RequireKeys<
AuthContextValue,
"user" | "permissions"
>;
export const useAuthenticated = (): AuthenticatedAuthContextValue => {
const auth = useAuthContext();
if (!auth.user) {
throw new Error("User is not authenticated.");
}
if (!auth.permissions) {
throw new Error("Permissions are not available.");
}
return auth as AuthenticatedAuthContextValue;
};
+1
View File
@@ -1,3 +1,4 @@
export * from "./useAuthenticated";
export * from "./useClickable";
export * from "./useClickableTableRow";
export * from "./useClipboard";
+29
View File
@@ -0,0 +1,29 @@
import {
type AuthContextValue,
useAuthContext,
} from "contexts/auth/AuthProvider";
type RequireKeys<T, R extends keyof T> = Omit<T, R> & {
[K in keyof Pick<T, R>]-?: NonNullable<T[K]>;
};
// We can do some TS magic here but I would rather to be explicit on what
// values are not undefined when authenticated
type AuthenticatedAuthContextValue = RequireKeys<
AuthContextValue,
"user" | "permissions"
>;
export const useAuthenticated = (): AuthenticatedAuthContextValue => {
const auth = useAuthContext();
if (!auth.user) {
throw new Error("User is not authenticated.");
}
if (!auth.permissions) {
throw new Error("Permissions are not available.");
}
return auth as AuthenticatedAuthContextValue;
};
@@ -3,7 +3,7 @@ import Link from "@mui/material/Link";
import Snackbar from "@mui/material/Snackbar";
import { Button } from "components/Button/Button";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { AnnouncementBanners } from "modules/dashboard/AnnouncementBanners/AnnouncementBanners";
import { LicenseBanner } from "modules/dashboard/LicenseBanner/LicenseBanner";
import { type FC, type HTMLAttributes, Suspense } from "react";
@@ -10,7 +10,7 @@ import type {
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { canViewAnyOrganization } from "modules/permissions";
import { type FC, type PropsWithChildren, createContext } from "react";
@@ -1,6 +1,6 @@
import { health } from "api/queries/debug";
import { deploymentStats } from "api/queries/deployment";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { FC } from "react";
import { useQuery } from "react-query";
import { DeploymentBannerView } from "./DeploymentBannerView";
+1 -1
View File
@@ -1,6 +1,6 @@
import { buildInfo } from "api/queries/buildInfo";
import { useProxy } from "contexts/ProxyContext";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { useDashboard } from "modules/dashboard/useDashboard";
import { canViewDeploymentSettings } from "modules/permissions";
@@ -10,7 +10,7 @@ import { Button } from "components/Button/Button";
import { displayError } from "components/GlobalSnackbar/utils";
import { Latency } from "components/Latency/Latency";
import type { ProxyContextValue } from "contexts/ProxyContext";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { ChevronDownIcon } from "lucide-react";
import { type FC, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
@@ -6,7 +6,7 @@ import {
BreadcrumbSeparator,
} from "components/Breadcrumb/Breadcrumb";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { canViewDeploymentSettings } from "modules/permissions";
import { RequirePermission } from "modules/permissions/RequirePermission";
import { type FC, Suspense } from "react";
@@ -1,4 +1,4 @@
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
import { DeploymentSidebarView } from "./DeploymentSidebarView";
@@ -1,5 +1,5 @@
import { Sidebar as BaseSidebar } from "components/Sidebar/Sidebar";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
import type { FC } from "react";
import { OrganizationSidebarView } from "./OrganizationSidebarView";
@@ -2,11 +2,12 @@ import { templateByName } from "api/queries/templates";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
import { type FC, createContext } from "react";
import type { FC } from "react";
import { useQuery } from "react-query";
import { useParams } from "react-router-dom";
import CreateWorkspacePage from "./CreateWorkspacePage";
import CreateWorkspacePageExperimental from "./CreateWorkspacePageExperimental";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
const CreateWorkspaceExperimentRouter: FC = () => {
const { experiments } = useDashboard();
@@ -70,7 +71,3 @@ const CreateWorkspaceExperimentRouter: FC = () => {
export default CreateWorkspaceExperimentRouter;
const optOutKey = (id: string) => `parameters.${id}.optOut`;
export const ExperimentalFormContext = createContext<
{ toggleOptedOut: () => void } | undefined
>(undefined);
@@ -14,7 +14,7 @@ import type {
Workspace,
} from "api/typesGenerated";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEffectEvent } from "hooks/hookPolyfills";
import { useDashboard } from "modules/dashboard/useDashboard";
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
@@ -12,7 +12,7 @@ import type {
Workspace,
} from "api/typesGenerated";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEffectEvent } from "hooks/hookPolyfills";
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
import {
@@ -47,11 +47,11 @@ import {
useValidationSchemaForRichParameters,
} from "utils/richParameters";
import * as Yup from "yup";
import { ExperimentalFormContext } from "./CreateWorkspaceExperimentRouter";
import type {
CreateWorkspaceMode,
ExternalAuthPollingState,
} from "./CreateWorkspacePage";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
import { ExternalAuthButton } from "./ExternalAuthButton";
import type { CreateWorkspacePermissions } from "./permissions";
@@ -33,11 +33,11 @@ import {
import { getFormHelpers, nameValidator } from "utils/formUtils";
import type { AutofillBuildParameter } from "utils/richParameters";
import * as Yup from "yup";
import { ExperimentalFormContext } from "./CreateWorkspaceExperimentRouter";
import type {
CreateWorkspaceMode,
ExternalAuthPollingState,
} from "./CreateWorkspacePage";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
import { ExternalAuthButton } from "./ExternalAuthButton";
import type { CreateWorkspacePermissions } from "./permissions";
@@ -0,0 +1,5 @@
import { createContext } from "react";
export const ExperimentalFormContext = createContext<
{ toggleOptedOut: () => void } | undefined
>(undefined);
@@ -12,7 +12,7 @@ import {
} from "components/GitDeviceAuth/GitDeviceAuth";
import { SignInLayout } from "components/SignInLayout/SignInLayout";
import { Welcome } from "components/Welcome/Welcome";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { FC } from "react";
import { useMemo } from "react";
import { useQuery, useQueryClient } from "react-query";
+9
View File
@@ -0,0 +1,9 @@
export const Language = {
emailLabel: "Email",
passwordLabel: "Password",
emailInvalid: "Please enter a valid email address.",
emailRequired: "Please enter an email address.",
passwordSignIn: "Sign In",
githubSignIn: "GitHub",
oidcSignIn: "OpenID Connect",
};
+1 -1
View File
@@ -8,8 +8,8 @@ import {
waitForLoaderToBeRemoved,
} from "testHelpers/renderHelpers";
import { server } from "testHelpers/server";
import { Language } from "./Language";
import { LoginPage } from "./LoginPage";
import { Language } from "./SignInForm";
describe("LoginPage", () => {
beforeEach(() => {
+1 -1
View File
@@ -4,7 +4,7 @@ import Button from "@mui/material/Button";
import { visuallyHidden } from "@mui/utils";
import type { AuthMethods } from "api/typesGenerated";
import { type FC, useId } from "react";
import { Language } from "./SignInForm";
import { Language } from "./Language";
const iconStyles = {
width: 16,
@@ -7,7 +7,7 @@ import type { FC } from "react";
import { Link as RouterLink } from "react-router-dom";
import { getFormHelpers, onChangeTrimmed } from "utils/formUtils";
import * as Yup from "yup";
import { Language } from "./SignInForm";
import { Language } from "./Language";
type PasswordSignInFormProps = {
onSubmit: (credentials: { email: string; password: string }) => void;
-10
View File
@@ -7,16 +7,6 @@ import { getApplicationName } from "utils/appearance";
import { OAuthSignInForm } from "./OAuthSignInForm";
import { PasswordSignInForm } from "./PasswordSignInForm";
export const Language = {
emailLabel: "Email",
passwordLabel: "Password",
emailInvalid: "Please enter a valid email address.",
emailRequired: "Please enter an email address.",
passwordSignIn: "Sign In",
githubSignIn: "GitHub",
oidcSignIn: "OpenID Connect",
};
const styles = {
root: {
width: "100%",
@@ -1,6 +1,6 @@
import { createOrganization } from "api/queries/organizations";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import { RequirePermission } from "modules/permissions/RequirePermission";
import type { FC } from "react";
@@ -13,7 +13,7 @@ import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { EmptyState } from "components/EmptyState/EmptyState";
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { usePaginatedQuery } from "hooks/usePaginatedQuery";
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
import { RequirePermission } from "modules/permissions/RequirePermission";
@@ -5,7 +5,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { Margins } from "components/Margins/Margins";
import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import {
type WorkspacePermissions,
workspacePermissionChecks,
@@ -4,7 +4,7 @@ import {
templateVersion,
templateVersionByName,
} from "api/queries/templates";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { linkToTemplate, useLinks } from "modules/navigation";
import { type FC, useMemo } from "react";
import { Helmet } from "react-helmet-async";
@@ -1,7 +1,7 @@
import { workspacePermissionsByOrganization } from "api/queries/organizations";
import { templateExamples, templates } from "api/queries/templates";
import { useFilter } from "components/Filter/Filter";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
@@ -1,7 +1,7 @@
import { groupsForUser } from "api/queries/groups";
import { Stack } from "components/Stack/Stack";
import { useAuthContext } from "contexts/auth/AuthProvider";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
import { useQuery } from "react-query";
+1 -1
View File
@@ -1,7 +1,7 @@
import { Loader } from "components/Loader/Loader";
import { Margins } from "components/Margins/Margins";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { type FC, Suspense } from "react";
import { Helmet } from "react-helmet-async";
import { Outlet } from "react-router-dom";
@@ -22,7 +22,7 @@ import type {
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import {
castNotificationMethod,
methodIcons,
@@ -2,7 +2,7 @@ import { getErrorMessage } from "api/errors";
import { getApps, revokeApp } from "api/queries/oauth2";
import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog";
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { type FC, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { Section } from "../Section";
@@ -5,7 +5,7 @@ import {
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { FC } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { Section } from "../Section";
@@ -3,7 +3,7 @@ import { authMethods, updatePassword } from "api/queries/users";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { ComponentProps, FC } from "react";
import { useMutation, useQuery } from "react-query";
import { Section } from "../Section";
+1 -1
View File
@@ -17,7 +17,7 @@ import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog";
import { useFilter } from "components/Filter/Filter";
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { isNonInitialPage } from "components/PaginationWidget/utils";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { usePaginatedQuery } from "hooks/usePaginatedQuery";
import { useDashboard } from "modules/dashboard/useDashboard";
import { type FC, useState } from "react";
@@ -22,8 +22,8 @@ import {
import { displayError } from "components/GlobalSnackbar/utils";
import { MemoizedInlineMarkdown } from "components/Markdown/Markdown";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import dayjs from "dayjs";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
@@ -3,7 +3,7 @@ import { templates } from "api/queries/templates";
import type { Workspace } from "api/typesGenerated";
import { useFilter } from "components/Filter/Filter";
import { useUserFilterMenu } from "components/Filter/UserFilter";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEffectEvent } from "hooks/hookPolyfills";
import { usePagination } from "hooks/usePagination";
import { useDashboard } from "modules/dashboard/useDashboard";