From 25cc464e5da24773df86d131543348e6266a3b5e Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 16:51:26 +0000 Subject: [PATCH 1/7] feat(site/experimental): add an organization switcher to the user menu --- site/src/api/queries/users.ts | 7 ++++ site/src/contexts/auth/RequireAuth.tsx | 2 +- .../Navbar/UserDropdown/UserDropdown.tsx | 4 ++ .../UserDropdown/UserDropdownContent.tsx | 42 ++++++++++++++++++- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/site/src/api/queries/users.ts b/site/src/api/queries/users.ts index 7dcd157f7bc6c..cf70038e7ca23 100644 --- a/site/src/api/queries/users.ts +++ b/site/src/api/queries/users.ts @@ -249,3 +249,10 @@ export const updateAppearanceSettings = ( }, }; }; + +export const myOrganizations = () => { + return { + queryKey: ["organizations", "me"], + queryFn: () => API.getOrganizations(), + }; +}; diff --git a/site/src/contexts/auth/RequireAuth.tsx b/site/src/contexts/auth/RequireAuth.tsx index b1def94fd9485..6172ba8212ac5 100644 --- a/site/src/contexts/auth/RequireAuth.tsx +++ b/site/src/contexts/auth/RequireAuth.tsx @@ -67,7 +67,7 @@ export const RequireAuth: FC = () => { }; type RequireKeys = Omit & { - [K in keyof Pick]: NonNullable; + [K in keyof Pick]-?: NonNullable; }; // We can do some TS magic here but I would rather to be explicit on what diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx index b42858157d142..3f07ab098c981 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx @@ -1,6 +1,7 @@ import { css, type Interpolation, type Theme, useTheme } from "@emotion/react"; import Badge from "@mui/material/Badge"; import type { FC } from "react"; +import { useQuery } from "react-query"; import type * as TypesGen from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { @@ -11,6 +12,7 @@ import { import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants"; import { UserDropdownContent } from "./UserDropdownContent"; +import { myOrganizations } from "api/queries/users"; export interface UserDropdownProps { user: TypesGen.User; @@ -26,6 +28,7 @@ export const UserDropdown: FC = ({ onSignOut, }) => { const theme = useTheme(); + const organizationsQuery = useQuery(myOrganizations()); return ( @@ -63,6 +66,7 @@ export const UserDropdown: FC = ({ user={user} buildInfo={buildInfo} supportLinks={supportLinks} + organizations={organizationsQuery.data} onSignOut={onSignOut} /> diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx index fa3f64c37cb18..28c2ffc366664 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx @@ -20,6 +20,7 @@ import { CopyButton } from "components/CopyButton/CopyButton"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; import { usePopover } from "components/Popover/Popover"; import { Stack } from "components/Stack/Stack"; +import { useDashboard } from "modules/dashboard/useDashboard"; export const Language = { accountLabel: "Account", @@ -84,18 +85,21 @@ const styles = { export interface UserDropdownContentProps { user: TypesGen.User; + organizations?: TypesGen.Organization[]; buildInfo?: TypesGen.BuildInfoResponse; supportLinks?: readonly TypesGen.LinkConfig[]; onSignOut: () => void; } export const UserDropdownContent: FC = ({ - buildInfo, user, + organizations, + buildInfo, supportLinks, onSignOut, }) => { const popover = usePopover(); + const { organizationId, setOrganizationId } = useDashboard(); const onPopoverClose = () => { popover.setIsOpen(false); @@ -128,6 +132,42 @@ export const UserDropdownContent: FC = ({ + {organizations && ( +
+
+ My teams +
+ {organizations.map((org) => ( + { + setOrganizationId(org.id); + popover.setIsOpen(false); + }} + > + {/* */} + + {org.name} + {organizationId == org.id && ( + Current + )} + + + ))} +
+ )} + + + From c95dbed87df3a20889c12016ff13f42df119d0d6 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 22:46:37 +0000 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx | 2 +- .../dashboard/Navbar/UserDropdown/UserDropdownContent.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx index 3f07ab098c981..3b0b0d09686c1 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx @@ -2,6 +2,7 @@ import { css, type Interpolation, type Theme, useTheme } from "@emotion/react"; import Badge from "@mui/material/Badge"; import type { FC } from "react"; import { useQuery } from "react-query"; +import { myOrganizations } from "api/queries/users"; import type * as TypesGen from "api/typesGenerated"; import { DropdownArrow } from "components/DropdownArrow/DropdownArrow"; import { @@ -12,7 +13,6 @@ import { import { UserAvatar } from "components/UserAvatar/UserAvatar"; import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants"; import { UserDropdownContent } from "./UserDropdownContent"; -import { myOrganizations } from "api/queries/users"; export interface UserDropdownProps { user: TypesGen.User; diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx index 28c2ffc366664..94ed4a56ea29c 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx @@ -157,7 +157,7 @@ export const UserDropdownContent: FC = ({ {/* */} {org.name} - {organizationId == org.id && ( + {organizationId === org.id && ( Current )} @@ -166,7 +166,7 @@ export const UserDropdownContent: FC = ({ )} - + From eea7fd1b47c9481ed58cee1b139b4787ef8a5970 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 23:17:28 +0000 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=A7=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/Navbar/UserDropdown/UserDropdown.tsx | 9 ++++++++- .../Navbar/UserDropdown/UserDropdownContent.tsx | 8 +++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx index 3b0b0d09686c1..1efdfec9df0ed 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx @@ -11,6 +11,7 @@ import { PopoverTrigger, } from "components/Popover/Popover"; import { UserAvatar } from "components/UserAvatar/UserAvatar"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants"; import { UserDropdownContent } from "./UserDropdownContent"; @@ -28,7 +29,11 @@ export const UserDropdown: FC = ({ onSignOut, }) => { const theme = useTheme(); - const organizationsQuery = useQuery(myOrganizations()); + const organizationsQuery = useQuery({ + ...myOrganizations(), + enabled: Boolean(localStorage.getItem("enableMultiOrganizationUi")), + }); + const { organizationId, setOrganizationId } = useDashboard(); return ( @@ -67,6 +72,8 @@ export const UserDropdown: FC = ({ buildInfo={buildInfo} supportLinks={supportLinks} organizations={organizationsQuery.data} + organizationId={organizationId} + setOrganizationId={setOrganizationId} onSignOut={onSignOut} /> diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx index 94ed4a56ea29c..bdf7bd83854b0 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx @@ -20,7 +20,6 @@ import { CopyButton } from "components/CopyButton/CopyButton"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; import { usePopover } from "components/Popover/Popover"; import { Stack } from "components/Stack/Stack"; -import { useDashboard } from "modules/dashboard/useDashboard"; export const Language = { accountLabel: "Account", @@ -86,6 +85,8 @@ const styles = { export interface UserDropdownContentProps { user: TypesGen.User; organizations?: TypesGen.Organization[]; + organizationId?: string; + setOrganizationId?: (id: string) => void; buildInfo?: TypesGen.BuildInfoResponse; supportLinks?: readonly TypesGen.LinkConfig[]; onSignOut: () => void; @@ -94,12 +95,13 @@ export interface UserDropdownContentProps { export const UserDropdownContent: FC = ({ user, organizations, + organizationId, + setOrganizationId, buildInfo, supportLinks, onSignOut, }) => { const popover = usePopover(); - const { organizationId, setOrganizationId } = useDashboard(); const onPopoverClose = () => { popover.setIsOpen(false); @@ -150,7 +152,7 @@ export const UserDropdownContent: FC = ({ key={org.id} css={styles.menuItem} onClick={() => { - setOrganizationId(org.id); + setOrganizationId?.(org.id); popover.setIsOpen(false); }} > From 063f39420bf27371835c8191f5942da9f13dba58 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 23:27:58 +0000 Subject: [PATCH 4/7] fix divider spacing --- .../UserDropdown/UserDropdownContent.tsx | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx index bdf7bd83854b0..8dc0f23d34f73 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx @@ -135,41 +135,42 @@ export const UserDropdownContent: FC = ({ {organizations && ( -
-
- My teams -
- {organizations.map((org) => ( - { - setOrganizationId?.(org.id); - popover.setIsOpen(false); + <> +
+
- {/* */} - - {org.name} - {organizationId === org.id && ( - Current - )} - - - ))} -
+ My teams +
+ {organizations.map((org) => ( + { + setOrganizationId?.(org.id); + popover.setIsOpen(false); + }} + > + {/* */} + + {org.name} + {organizationId === org.id && ( + Current + )} + + + ))} +
+ + )} - - From b9378d1c34f058ff6846ab2db33d2ddc84ba4eb8 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 23:33:27 +0000 Subject: [PATCH 5/7] yet again --- .../dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx index 04e18fc3d49f0..fb9deab4d2532 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, screen, userEvent, within, waitFor } from "@storybook/test"; import { MockBuildInfo, MockUser } from "testHelpers/entities"; import { UserDropdown } from "./UserDropdown"; +import { withDashboardProvider } from "testHelpers/storybook"; const meta: Meta = { title: "modules/dashboard/UserDropdown", @@ -16,6 +17,7 @@ const meta: Meta = { { icon: "/icon/aws.svg", name: "Amazon Web Services", target: "" }, ], }, + decorators: [withDashboardProvider], }; export default meta; From af0c1263298e2cc9860539c7827488b0abcad7bb Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 23:37:49 +0000 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx index fb9deab4d2532..c7652eb460c77 100644 --- a/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx +++ b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx @@ -1,8 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, screen, userEvent, within, waitFor } from "@storybook/test"; import { MockBuildInfo, MockUser } from "testHelpers/entities"; -import { UserDropdown } from "./UserDropdown"; import { withDashboardProvider } from "testHelpers/storybook"; +import { UserDropdown } from "./UserDropdown"; const meta: Meta = { title: "modules/dashboard/UserDropdown", From 4c59f88dc9d437387f537ad6048e0e62ac6d92cb Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Tue, 14 May 2024 23:49:17 +0000 Subject: [PATCH 7/7] I'm so tired --- site/src/modules/dashboard/Navbar/NavbarView.stories.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx index cf5522c56203a..2490234bd36e1 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { chromaticWithTablet } from "testHelpers/chromatic"; import { MockUser, MockUser2 } from "testHelpers/entities"; +import { withDashboardProvider } from "testHelpers/storybook"; import { NavbarView } from "./NavbarView"; const meta: Meta = { @@ -10,6 +11,7 @@ const meta: Meta = { args: { user: MockUser, }, + decorators: [withDashboardProvider], }; export default meta;