From 07babacec0404b45217ea3be14f803a6ce7c6f07 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 17 May 2023 14:41:13 +0000 Subject: [PATCH 1/2] Add base for proxies --- site/src/components/Navbar/Navbar.test.tsx | 2 +- site/src/components/Navbar/Navbar.tsx | 5 +- .../NavbarView.stories.tsx | 0 .../NavbarView.test.tsx | 26 ++- .../{NavbarView => Navbar}/NavbarView.tsx | 171 +++++++++++++++++- .../components/UsersLayout/UsersLayout.tsx | 2 +- site/src/contexts/ProxyContext.tsx | 2 +- 7 files changed, 193 insertions(+), 15 deletions(-) rename site/src/components/{NavbarView => Navbar}/NavbarView.stories.tsx (100%) rename site/src/components/{NavbarView => Navbar}/NavbarView.test.tsx (81%) rename site/src/components/{NavbarView => Navbar}/NavbarView.tsx (56%) 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..f34108da33de4 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,7 @@ export const Navbar: FC = () => { featureVisibility["audit_log"] && Boolean(permissions.viewAuditLog) const canViewDeployment = Boolean(permissions.viewDeploymentValues) const onSignOut = () => authSend("SIGN_OUT") + const proxyContextValue = useProxy() return ( { onSignOut={onSignOut} canViewAuditLog={canViewAuditLog} canViewDeployment={canViewDeployment} + proxyContextValue={proxyContextValue} /> ) } 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 81% rename from site/src/components/NavbarView/NavbarView.test.tsx rename to site/src/components/Navbar/NavbarView.test.tsx index cae48b7a8ddcf..41f50487590b2 100644 --- a/site/src/components/NavbarView/NavbarView.test.tsx +++ b/site/src/components/Navbar/NavbarView.test.tsx @@ -1,7 +1,23 @@ 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" + +const proxyContextValue: ProxyContextValue = { + proxy: { + preferredPathAppURL: "", + preferredWildcardHostname: "", + selectedProxy: MockPrimaryWorkspaceProxy, + }, + isLoading: false, + isFetched: true, + setProxy: jest.fn(), +} describe("NavbarView", () => { const noop = () => { @@ -23,6 +39,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 +96,7 @@ const NavItems: React.FC< ) } -export const NavbarView: React.FC> = ({ +export const NavbarView: FC = ({ user, logo_url, buildInfo, @@ -91,6 +104,7 @@ export const NavbarView: React.FC> = ({ onSignOut, canViewAuditLog, canViewDeployment, + proxyContextValue, }) => { const styles = useStyles() const [isDrawerOpen, setIsDrawerOpen] = useState(false) @@ -145,7 +159,14 @@ export const NavbarView: React.FC> = ({ canViewDeployment={canViewDeployment} /> -
+ + {user && ( > = ({ onSignOut={onSignOut} /> )} -
+ ) } +const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({ + proxyContextValue, +}) => { + const buttonRef = useRef(null) + const [isOpen, setIsOpen] = useState(false) + const selectedProxy = proxyContextValue.proxy.selectedProxy + + const closeMenu = () => setIsOpen(false) + + 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 === proxyContextValue.proxy.selectedProxy?.id} + sx={{ + "& .MuiSvgIcon-root": { fontSize: 16 }, + }} + > + + + + + {proxy.display_name} + + + + ))} + + + ) +} + +const ProxyStatusIcon: FC< + { proxy: TypesGen.Region; latency: number } & SvgIconProps +> = ({ proxy, latency, ...svgProps }) => { + if (!proxy.healthy) { + return ( + theme.palette.warning.light, ...svgProps.sx }} + /> + ) + } + + if (latency >= 150 && latency < 300) { + return ( + theme.palette.warning.light, ...svgProps.sx }} + /> + ) + } + + if (latency >= 300) { + return ( + theme.palette.error.light, ...svgProps.sx }} + /> + ) + } + + return ( + theme.palette.success.light, ...svgProps.sx }} + /> + ) +} + const useStyles = makeStyles((theme) => ({ root: { height: navHeight, @@ -192,12 +349,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 66390164809a6..0c9b0693ac280 100644 --- a/site/src/contexts/ProxyContext.tsx +++ b/site/src/contexts/ProxyContext.tsx @@ -11,7 +11,7 @@ import { } from "react" import { ProxyLatencyReport, useProxyLatency } from "./useProxyLatency" -interface ProxyContextValue { +export interface ProxyContextValue { proxy: PreferredProxy proxies?: Region[] proxyLatencies?: Record From a096f9fdd8e2dcee760d8cab9dba455ab83c7529 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 30 May 2023 13:41:37 +0000 Subject: [PATCH 2/2] Add proxy navbar menu --- site/src/components/Navbar/Navbar.tsx | 5 +- .../src/components/Navbar/NavbarView.test.tsx | 5 +- site/src/components/Navbar/NavbarView.tsx | 116 ++++++++++-------- site/src/contexts/ProxyContext.tsx | 2 +- site/src/contexts/useProxyLatency.ts | 6 +- .../WorkspaceProxyPage/WorkspaceProxyRow.tsx | 44 ++++--- .../WorkspaceProxyPage/WorkspaceProxyView.tsx | 4 +- 7 files changed, 109 insertions(+), 73 deletions(-) diff --git a/site/src/components/Navbar/Navbar.tsx b/site/src/components/Navbar/Navbar.tsx index f34108da33de4..88042b5bceb8d 100644 --- a/site/src/components/Navbar/Navbar.tsx +++ b/site/src/components/Navbar/Navbar.tsx @@ -18,6 +18,7 @@ export const Navbar: FC = () => { const canViewDeployment = Boolean(permissions.viewDeploymentValues) const onSignOut = () => authSend("SIGN_OUT") const proxyContextValue = useProxy() + const dashboard = useDashboard() return ( { onSignOut={onSignOut} canViewAuditLog={canViewAuditLog} canViewDeployment={canViewDeployment} - proxyContextValue={proxyContextValue} + proxyContextValue={ + dashboard.experiments.includes("moons") ? proxyContextValue : undefined + } /> ) } diff --git a/site/src/components/Navbar/NavbarView.test.tsx b/site/src/components/Navbar/NavbarView.test.tsx index 41f50487590b2..7c5bf6507fd78 100644 --- a/site/src/components/Navbar/NavbarView.test.tsx +++ b/site/src/components/Navbar/NavbarView.test.tsx @@ -7,16 +7,19 @@ import { 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: "", - selectedProxy: MockPrimaryWorkspaceProxy, + proxy: MockPrimaryWorkspaceProxy, }, isLoading: false, isFetched: true, setProxy: jest.fn(), + clearProxy: action("clearProxy"), + proxyLatencies: {}, } describe("NavbarView", () => { diff --git a/site/src/components/Navbar/NavbarView.tsx b/site/src/components/Navbar/NavbarView.tsx index 861bcceaf804c..176a9ae446aad 100644 --- a/site/src/components/Navbar/NavbarView.tsx +++ b/site/src/components/Navbar/NavbarView.tsx @@ -2,11 +2,11 @@ import Drawer from "@mui/material/Drawer" import IconButton from "@mui/material/IconButton" import List from "@mui/material/List" import ListItem from "@mui/material/ListItem" -import { makeStyles } from "@mui/styles" +import { makeStyles, useTheme } from "@mui/styles" import MenuIcon from "@mui/icons-material/Menu" import { CoderIcon } from "components/Icons/CoderIcon" import { FC, useRef, useState } from "react" -import { NavLink, useLocation } from "react-router-dom" +import { NavLink, useLocation, useNavigate } from "react-router-dom" import { colors } from "theme/colors" import * as TypesGen from "../../api/typesGenerated" import { navHeight } from "../../theme/constants" @@ -19,11 +19,10 @@ import MenuItem from "@mui/material/MenuItem" import KeyboardArrowDownOutlined from "@mui/icons-material/KeyboardArrowDownOutlined" import { ProxyContextValue } from "contexts/ProxyContext" import { displayError } from "components/GlobalSnackbar/utils" -import SignalCellular1BarOutlined from "@mui/icons-material/SignalCellular1BarOutlined" -import SignalCellular2BarOutlined from "@mui/icons-material/SignalCellular2BarOutlined" -import SignalCellular4BarOutlined from "@mui/icons-material/SignalCellular4BarOutlined" -import SignalCellularConnectedNoInternet0BarOutlined from "@mui/icons-material/SignalCellularConnectedNoInternet0BarOutlined" -import { SvgIconProps } from "@mui/material/SvgIcon" +import Divider from "@mui/material/Divider" +import HelpOutline from "@mui/icons-material/HelpOutline" +import Tooltip from "@mui/material/Tooltip" +import Skeleton from "@mui/material/Skeleton" export const USERS_LINK = `/users?filter=${encodeURIComponent("status:active")}` @@ -35,7 +34,7 @@ export interface NavbarViewProps { onSignOut: () => void canViewAuditLog: boolean canViewDeployment: boolean - proxyContextValue: ProxyContextValue + proxyContextValue?: ProxyContextValue } export const Language = { @@ -166,7 +165,9 @@ export const NavbarView: FC = ({ alignItems="center" paddingRight={2} > - + {proxyContextValue && ( + + )} {user && ( = ({ }) => { const buttonRef = useRef(null) const [isOpen, setIsOpen] = useState(false) - const selectedProxy = proxyContextValue.proxy.selectedProxy - + const selectedProxy = proxyContextValue.proxy.proxy const closeMenu = () => setIsOpen(false) + const navigate = useNavigate() + + if (!proxyContextValue.isFetched) { + return ( + + ) + } return ( <> @@ -198,11 +209,12 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({ size="small" endIcon={} sx={{ + borderRadius: "4px", "& .MuiSvgIcon-root": { fontSize: 14 }, }} > {selectedProxy ? ( - + = ({ /> {selectedProxy.display_name} - @@ -231,6 +242,7 @@ const ProxyMenu: FC<{ proxyContextValue: ProxyContextValue }> = ({ anchorEl={buttonRef.current} onClick={closeMenu} onClose={closeMenu} + sx={{ "& .MuiMenu-paper": { py: 1 } }} > {proxyContextValue.proxies?.map((proxy) => ( = ({ closeMenu() }} key={proxy.id} - selected={proxy.id === proxyContextValue.proxy.selectedProxy?.id} + selected={proxy.id === selectedProxy?.id} sx={{ - "& .MuiSvgIcon-root": { fontSize: 16 }, + fontSize: 14, }} > - - + + = ({ /> {proxy.display_name} - ))} + theme.palette.divider }} /> + { + navigate("/settings/workspace-proxies") + }} + > + Proxy settings + ) } -const ProxyStatusIcon: FC< - { proxy: TypesGen.Region; latency: number } & SvgIconProps -> = ({ proxy, latency, ...svgProps }) => { - if (!proxy.healthy) { - return ( - theme.palette.warning.light, ...svgProps.sx }} - /> - ) - } +const ProxyStatusLatency: FC<{ proxy: TypesGen.Region; latency?: number }> = ({ + proxy, + latency, +}) => { + const theme = useTheme() + let color = theme.palette.success.light - if (latency >= 150 && latency < 300) { + if (!latency) { return ( - theme.palette.warning.light, ...svgProps.sx }} - /> + + theme.palette.text.secondary, + }} + /> + ) } if (latency >= 300) { - return ( - theme.palette.error.light, ...svgProps.sx }} - /> - ) + color = theme.palette.error.light + } + + if (!proxy.healthy || latency >= 100) { + color = theme.palette.warning.light } return ( - theme.palette.success.light, ...svgProps.sx }} - /> + + {latency.toFixed(0)}ms + ) } 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 + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy