diff --git a/site/src/components/Navbar/Navbar.test.tsx b/site/src/components/Navbar/Navbar.test.tsx index ca7001af5dad7..8bdac69e67443 100644 --- a/site/src/components/Navbar/Navbar.test.tsx +++ b/site/src/components/Navbar/Navbar.test.tsx @@ -1,6 +1,6 @@ import { render, screen, waitFor } from "@testing-library/react" import { App } from "app" -import { Language } from "components/NavbarView/NavbarView" +import { Language } from "./NavbarView" import { rest } from "msw" import { MockEntitlementsWithAuditLog, diff --git a/site/src/components/Navbar/Navbar.tsx b/site/src/components/Navbar/Navbar.tsx index 3e25eed4cb0c0..88042b5bceb8d 100644 --- a/site/src/components/Navbar/Navbar.tsx +++ b/site/src/components/Navbar/Navbar.tsx @@ -4,7 +4,8 @@ import { useFeatureVisibility } from "hooks/useFeatureVisibility" import { useMe } from "hooks/useMe" import { usePermissions } from "hooks/usePermissions" import { FC } from "react" -import { NavbarView } from "../NavbarView/NavbarView" +import { NavbarView } from "./NavbarView" +import { useProxy } from "contexts/ProxyContext" export const Navbar: FC = () => { const { appearance, buildInfo } = useDashboard() @@ -16,6 +17,8 @@ export const Navbar: FC = () => { featureVisibility["audit_log"] && Boolean(permissions.viewAuditLog) const canViewDeployment = Boolean(permissions.viewDeploymentValues) const onSignOut = () => authSend("SIGN_OUT") + const proxyContextValue = useProxy() + const dashboard = useDashboard() return ( { onSignOut={onSignOut} canViewAuditLog={canViewAuditLog} canViewDeployment={canViewDeployment} + proxyContextValue={ + dashboard.experiments.includes("moons") ? proxyContextValue : undefined + } /> ) } diff --git a/site/src/components/NavbarView/NavbarView.stories.tsx b/site/src/components/Navbar/NavbarView.stories.tsx similarity index 100% rename from site/src/components/NavbarView/NavbarView.stories.tsx rename to site/src/components/Navbar/NavbarView.stories.tsx diff --git a/site/src/components/NavbarView/NavbarView.test.tsx b/site/src/components/Navbar/NavbarView.test.tsx similarity index 80% rename from site/src/components/NavbarView/NavbarView.test.tsx rename to site/src/components/Navbar/NavbarView.test.tsx index cae48b7a8ddcf..7c5bf6507fd78 100644 --- a/site/src/components/NavbarView/NavbarView.test.tsx +++ b/site/src/components/Navbar/NavbarView.test.tsx @@ -1,7 +1,26 @@ import { screen } from "@testing-library/react" -import { MockUser, MockUser2 } from "../../testHelpers/entities" +import { + MockPrimaryWorkspaceProxy, + MockUser, + MockUser2, +} from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" import { Language as navLanguage, NavbarView } from "./NavbarView" +import { ProxyContextValue } from "contexts/ProxyContext" +import { action } from "@storybook/addon-actions" + +const proxyContextValue: ProxyContextValue = { + proxy: { + preferredPathAppURL: "", + preferredWildcardHostname: "", + proxy: MockPrimaryWorkspaceProxy, + }, + isLoading: false, + isFetched: true, + setProxy: jest.fn(), + clearProxy: action("clearProxy"), + proxyLatencies: {}, +} describe("NavbarView", () => { const noop = () => { @@ -23,6 +42,7 @@ describe("NavbarView", () => { it("workspaces nav link has the correct href", async () => { render( { it("templates nav link has the correct href", async () => { render( { it("users nav link has the correct href", async () => { render( { // When render( { it("audit nav link has the correct href", async () => { render( { it("audit nav link is hidden for members", async () => { render( { it("deployment nav link has the correct href", async () => { render( { it("deployment nav link is hidden for members", async () => { render( void canViewAuditLog: boolean canViewDeployment: boolean + proxyContextValue?: ProxyContextValue } export const Language = { @@ -83,7 +95,7 @@ const NavItems: React.FC< ) } -export const NavbarView: React.FC> = ({ +export const NavbarView: FC = ({ user, logo_url, buildInfo, @@ -91,6 +103,7 @@ export const NavbarView: React.FC> = ({ onSignOut, canViewAuditLog, canViewDeployment, + proxyContextValue, }) => { const styles = useStyles() const [isDrawerOpen, setIsDrawerOpen] = useState(false) @@ -145,7 +158,16 @@ export const NavbarView: React.FC> = ({ canViewDeployment={canViewDeployment} /> -
+ + {proxyContextValue && ( + + )} {user && ( > = ({ onSignOut={onSignOut} /> )} -
+ ) } +const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({ + proxyContextValue, +}) => { + const buttonRef = useRef(null) + const [isOpen, setIsOpen] = useState(false) + const selectedProxy = proxyContextValue.proxy.proxy + const closeMenu = () => setIsOpen(false) + const navigate = useNavigate() + + if (!proxyContextValue.isFetched) { + return ( + + ) + } + + return ( + <> + + + {proxyContextValue.proxies?.map((proxy) => ( + { + if (!proxy.healthy) { + displayError("Please select a healthy workspace proxy.") + closeMenu() + return + } + + proxyContextValue.setProxy(proxy) + closeMenu() + }} + key={proxy.id} + selected={proxy.id === selectedProxy?.id} + sx={{ + fontSize: 14, + }} + > + + + + + {proxy.display_name} + + + + ))} + theme.palette.divider }} /> + { + navigate("/settings/workspace-proxies") + }} + > + Proxy settings + + + + ) +} + +const ProxyStatusLatency: FC<{ proxy: TypesGen.Region; latency?: number }> = ({ + proxy, + latency, +}) => { + const theme = useTheme() + let color = theme.palette.success.light + + if (!latency) { + return ( + + theme.palette.text.secondary, + }} + /> + + ) + } + + if (latency >= 300) { + color = theme.palette.error.light + } + + if (!proxy.healthy || latency >= 100) { + color = theme.palette.warning.light + } + + return ( + + {latency.toFixed(0)}ms + + ) +} + const useStyles = makeStyles((theme) => ({ root: { height: navHeight, @@ -192,12 +365,6 @@ const useStyles = makeStyles((theme) => ({ display: "flex", }, }, - profileButton: { - paddingRight: theme.spacing(2), - [theme.breakpoints.up("md")]: { - marginLeft: "auto", - }, - }, mobileMenuButton: { [theme.breakpoints.up("md")]: { display: "none", diff --git a/site/src/components/UsersLayout/UsersLayout.tsx b/site/src/components/UsersLayout/UsersLayout.tsx index e87d651840a54..a4eacd7b80d7d 100644 --- a/site/src/components/UsersLayout/UsersLayout.tsx +++ b/site/src/components/UsersLayout/UsersLayout.tsx @@ -4,7 +4,7 @@ import { makeStyles } from "@mui/styles" import GroupAdd from "@mui/icons-material/GroupAddOutlined" import PersonAdd from "@mui/icons-material/PersonAddOutlined" import { useMachine } from "@xstate/react" -import { USERS_LINK } from "components/NavbarView/NavbarView" +import { USERS_LINK } from "components/Navbar/NavbarView" import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader" import { useFeatureVisibility } from "hooks/useFeatureVisibility" import { usePermissions } from "hooks/usePermissions" diff --git a/site/src/contexts/ProxyContext.tsx b/site/src/contexts/ProxyContext.tsx index 32d3e3ae11d63..42de93234d9f7 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -127,8 +127,8 @@ export const ProxyProvider: FC = ({ children }) => { return ( => { // Just overwrite any existing latency. - state[action.proxyID] = action.report - return state + return { + ...state, + [action.proxyID]: action.report, + } } export const useProxyLatency = ( diff --git a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx index f23cb7ba8e727..8db1e9493af39 100644 --- a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx +++ b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx @@ -9,10 +9,11 @@ import { HealthyBadge, NotHealthyBadge, } from "components/DeploySettingsLayout/Badges" -import { makeStyles, useTheme } from "@mui/styles" +import { makeStyles } from "@mui/styles" import { combineClasses } from "utils/combineClasses" import { ProxyLatencyReport } from "contexts/useProxyLatency" import { getLatencyColor } from "utils/colors" +import { alpha } from "@mui/material/styles" export const ProxyRow: FC<{ latency?: ProxyLatencyReport @@ -21,7 +22,6 @@ export const ProxyRow: FC<{ preferred: boolean }> = ({ proxy, onSelectRegion, preferred, latency }) => { const styles = useStyles() - const theme = useTheme() const clickable = useClickableTableRow(() => { onSelectRegion(proxy) @@ -47,24 +47,32 @@ export const ProxyRow: FC<{ } avatar={ proxy.icon_url !== "" && ( - + ) } /> - {proxy.path_app_url} - + {proxy.path_app_url} + - - - {latency ? `${latency.latencyMS.toFixed(1)} ms` : "?"} - + + latency + ? getLatencyColor(theme, latency.latencyMS) + : theme.palette.text.secondary, + }} + > + {latency ? `${latency.latencyMS.toFixed(0)} ms` : "Not available"} ) @@ -83,9 +91,11 @@ const ProxyStatus: FC<{ const useStyles = makeStyles((theme) => ({ preferredrow: { - // TODO: What is the best way to show what proxy is currently being used? - backgroundColor: theme.palette.secondary.main, - outline: `3px solid ${theme.palette.secondary.light}`, - outlineOffset: -3, + backgroundColor: alpha( + theme.palette.primary.main, + theme.palette.action.hoverOpacity, + ), + outline: `1px solid ${theme.palette.primary.main}`, + outlineOffset: "-1px", }, })) diff --git a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx index 0ae853201328d..fc5ccf87d155e 100644 --- a/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx +++ b/site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx @@ -50,7 +50,9 @@ export const WorkspaceProxyView: FC< Proxy URL Status - Latency + + Latency +