diff --git a/site/src/contexts/auth/AuthProvider.tsx b/site/src/contexts/auth/AuthProvider.tsx index 767606e8d605f..2925ac095aadd 100644 --- a/site/src/contexts/auth/AuthProvider.tsx +++ b/site/src/contexts/auth/AuthProvider.tsx @@ -30,7 +30,7 @@ export type AuthContextValue = { isUpdatingProfile: boolean; user: User | undefined; permissions: Permissions | undefined; - organizationId: string | undefined; + organizationIds: readonly string[] | undefined; signInError: unknown; updateProfileError: unknown; signOut: () => void; @@ -119,7 +119,7 @@ export const AuthProvider: FC = ({ children }) => { permissions: permissionsQuery.data as Permissions | undefined, signInError: loginMutation.error, updateProfileError: updateProfileMutation.error, - organizationId: userQuery.data?.organization_ids[0], + organizationIds: userQuery.data?.organization_ids, }} > {children} diff --git a/site/src/contexts/auth/RequireAuth.test.tsx b/site/src/contexts/auth/RequireAuth.test.tsx index 0f128417a96f9..e1194cb601cbc 100644 --- a/site/src/contexts/auth/RequireAuth.test.tsx +++ b/site/src/contexts/auth/RequireAuth.test.tsx @@ -45,7 +45,7 @@ const createAuthWrapper = (override: Partial) => { isUpdatingProfile: false, permissions: undefined, authMethods: undefined, - organizationId: undefined, + organizationIds: undefined, signInError: undefined, updateProfileError: undefined, signOut: jest.fn(), @@ -95,6 +95,7 @@ describe("useAuthenticated", () => { wrapper: createAuthWrapper({ user: MockUser, permissions: MockPermissions, + organizationIds: [], }), }); }).not.toThrow(); diff --git a/site/src/contexts/auth/RequireAuth.tsx b/site/src/contexts/auth/RequireAuth.tsx index 2d6b14d3db69f..b1def94fd9485 100644 --- a/site/src/contexts/auth/RequireAuth.tsx +++ b/site/src/contexts/auth/RequireAuth.tsx @@ -66,15 +66,18 @@ export const RequireAuth: FC = () => { ); }; +type RequireKeys = Omit & { + [K in keyof Pick]: NonNullable; +}; + // We can do some TS magic here but I would rather to be explicit on what // values are not undefined when authenticated -type NonNullableAuth = AuthContextValue & { - user: Exclude; - permissions: Exclude; - organizationId: Exclude; -}; +type AuthenticatedAuthContextValue = RequireKeys< + AuthContextValue, + "user" | "permissions" | "organizationIds" +>; -export const useAuthenticated = (): NonNullableAuth => { +export const useAuthenticated = (): AuthenticatedAuthContextValue => { const auth = useAuthContext(); if (!auth.user) { @@ -85,5 +88,9 @@ export const useAuthenticated = (): NonNullableAuth => { throw new Error("Permissions are not available."); } - return auth as NonNullableAuth; + if (!auth.organizationIds) { + throw new Error("Organization ID is not available."); + } + + return auth as AuthenticatedAuthContextValue; }; diff --git a/site/src/modules/dashboard/DashboardProvider.tsx b/site/src/modules/dashboard/DashboardProvider.tsx index 19daf886f02f8..a44a162b994dd 100644 --- a/site/src/modules/dashboard/DashboardProvider.tsx +++ b/site/src/modules/dashboard/DashboardProvider.tsx @@ -1,4 +1,9 @@ -import { createContext, type FC, type PropsWithChildren } from "react"; +import { + createContext, + type FC, + type PropsWithChildren, + useState, +} from "react"; import { useQuery } from "react-query"; import { appearance } from "api/queries/appearance"; import { entitlements } from "api/queries/entitlements"; @@ -9,9 +14,13 @@ import type { Experiments, } from "api/typesGenerated"; import { Loader } from "components/Loader/Loader"; +import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useEffectEvent } from "hooks/hookPolyfills"; import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; export interface DashboardValue { + organizationId: string; + setOrganizationId: (id: string) => void; entitlements: Entitlements; experiments: Experiments; appearance: AppearanceConfig; @@ -23,6 +32,7 @@ export const DashboardContext = createContext( export const DashboardProvider: FC = ({ children }) => { const { metadata } = useEmbeddedMetadata(); + const { user, organizationIds } = useAuthenticated(); const entitlementsQuery = useQuery(entitlements(metadata.entitlements)); const experimentsQuery = useQuery(experiments(metadata.experiments)); const appearanceQuery = useQuery(appearance(metadata.appearance)); @@ -30,6 +40,23 @@ export const DashboardProvider: FC = ({ children }) => { const isLoading = !entitlementsQuery.data || !appearanceQuery.data || !experimentsQuery.data; + const lastUsedOrganizationId = localStorage.getItem( + `user:${user.id}.lastUsedOrganizationId`, + ); + const [activeOrganizationId, setActiveOrganizationId] = useState(() => + lastUsedOrganizationId && organizationIds.includes(lastUsedOrganizationId) + ? lastUsedOrganizationId + : organizationIds[0], + ); + + const setOrganizationId = useEffectEvent((id: string) => { + if (!organizationIds.includes(id)) { + throw new ReferenceError("Invalid organization ID"); + } + localStorage.setItem(`user:${user.id}.lastUsedOrganizationId`, id); + setActiveOrganizationId(id); + }); + if (isLoading) { return ; } @@ -37,6 +64,8 @@ export const DashboardProvider: FC = ({ children }) => { return ( = { @@ -29,19 +26,7 @@ const meta: Meta = { }, ], }, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [withDashboardProvider], }; export default meta; diff --git a/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx b/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx index 91ac28acc9127..d108d89ca1ba0 100644 --- a/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx +++ b/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx @@ -10,7 +10,6 @@ import { } from "api/queries/templates"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useDashboard } from "modules/dashboard/useDashboard"; import { CreateTemplateForm } from "./CreateTemplateForm"; import type { CreateTemplatePageViewProps } from "./types"; @@ -24,7 +23,7 @@ export const DuplicateTemplateView: FC = ({ isCreating, }) => { const navigate = useNavigate(); - const { organizationId } = useAuthenticated(); + const { entitlements, organizationId } = useDashboard(); const [searchParams] = useSearchParams(); const templateByNameQuery = useQuery( templateByName(organizationId, searchParams.get("fromTemplate")!), @@ -47,8 +46,7 @@ export const DuplicateTemplateView: FC = ({ templateVersionQuery.error || templateVersionVariablesQuery.error; - const dashboard = useDashboard(); - const formPermissions = getFormPermissions(dashboard.entitlements); + const formPermissions = getFormPermissions(entitlements); const isJobError = error instanceof JobError; const templateVersionLogsQuery = useQuery({ diff --git a/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx b/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx index a7212a1410d13..e62cda910f847 100644 --- a/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx +++ b/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx @@ -9,7 +9,6 @@ import { } from "api/queries/templates"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useDashboard } from "modules/dashboard/useDashboard"; import { CreateTemplateForm } from "./CreateTemplateForm"; import type { CreateTemplatePageViewProps } from "./types"; @@ -27,7 +26,7 @@ export const ImportStarterTemplateView: FC = ({ isCreating, }) => { const navigate = useNavigate(); - const { organizationId } = useAuthenticated(); + const { entitlements, organizationId } = useDashboard(); const [searchParams] = useSearchParams(); const templateExamplesQuery = useQuery(templateExamples(organizationId)); const templateExample = templateExamplesQuery.data?.find( @@ -37,8 +36,7 @@ export const ImportStarterTemplateView: FC = ({ const isLoading = templateExamplesQuery.isLoading; const loadingError = templateExamplesQuery.error; - const dashboard = useDashboard(); - const formPermissions = getFormPermissions(dashboard.entitlements); + const formPermissions = getFormPermissions(entitlements); const isJobError = error instanceof JobError; const templateVersionLogsQuery = useQuery({ diff --git a/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx b/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx index ac650baff112b..b9f49d4a46b94 100644 --- a/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx +++ b/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx @@ -7,7 +7,6 @@ import { JobError, templateVersionVariables, } from "api/queries/templates"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useDashboard } from "modules/dashboard/useDashboard"; import { CreateTemplateForm } from "./CreateTemplateForm"; import type { CreateTemplatePageViewProps } from "./types"; @@ -21,10 +20,9 @@ export const UploadTemplateView: FC = ({ error, }) => { const navigate = useNavigate(); - const { organizationId } = useAuthenticated(); - const dashboard = useDashboard(); - const formPermissions = getFormPermissions(dashboard.entitlements); + const { entitlements, organizationId } = useDashboard(); + const formPermissions = getFormPermissions(entitlements); const uploadFileMutation = useMutation(uploadFile()); const uploadedFile = uploadFileMutation.data; diff --git a/site/src/pages/CreateUserPage/CreateUserPage.tsx b/site/src/pages/CreateUserPage/CreateUserPage.tsx index 5de615241ebd0..bec3e7c637e05 100644 --- a/site/src/pages/CreateUserPage/CreateUserPage.tsx +++ b/site/src/pages/CreateUserPage/CreateUserPage.tsx @@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom"; import { authMethods, createUser } from "api/queries/users"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { Margins } from "components/Margins/Margins"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { CreateUserForm } from "./CreateUserForm"; @@ -14,7 +14,7 @@ export const Language = { }; export const CreateUserPage: FC = () => { - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const navigate = useNavigate(); const queryClient = useQueryClient(); const createUserMutation = useMutation(createUser(queryClient)); diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index df0bb38891f03..c2cd9ab9da3ae 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -35,10 +35,10 @@ export type ExternalAuthPollingState = "idle" | "polling" | "abandoned"; const CreateWorkspacePage: FC = () => { const { template: templateName } = useParams() as { template: string }; - const { user: me, organizationId } = useAuthenticated(); + const { user: me } = useAuthenticated(); const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); - const { experiments } = useDashboard(); + const { experiments, organizationId } = useDashboard(); const customVersionId = searchParams.get("version") ?? undefined; const defaultName = searchParams.get("name"); diff --git a/site/src/pages/GroupsPage/CreateGroupPage.tsx b/site/src/pages/GroupsPage/CreateGroupPage.tsx index 9a08da1cd4d0b..d5fd2c1f73c01 100644 --- a/site/src/pages/GroupsPage/CreateGroupPage.tsx +++ b/site/src/pages/GroupsPage/CreateGroupPage.tsx @@ -3,14 +3,14 @@ import { Helmet } from "react-helmet-async"; import { useMutation, useQueryClient } from "react-query"; import { useNavigate } from "react-router-dom"; import { createGroup } from "api/queries/groups"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import CreateGroupPageView from "./CreateGroupPageView"; export const CreateGroupPage: FC = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const createGroupMutation = useMutation(createGroup(queryClient)); return ( diff --git a/site/src/pages/GroupsPage/GroupsPage.tsx b/site/src/pages/GroupsPage/GroupsPage.tsx index 8ad9c2a3b00f5..41303d533bbbe 100644 --- a/site/src/pages/GroupsPage/GroupsPage.tsx +++ b/site/src/pages/GroupsPage/GroupsPage.tsx @@ -5,12 +5,14 @@ import { getErrorMessage } from "api/errors"; import { groups } from "api/queries/groups"; import { displayError } from "components/GlobalSnackbar/utils"; import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; import { pageTitle } from "utils/page"; import GroupsPageView from "./GroupsPageView"; export const GroupsPage: FC = () => { - const { organizationId, permissions } = useAuthenticated(); + const { permissions } = useAuthenticated(); + const { organizationId } = useDashboard(); const { createGroup: canCreateGroup } = permissions; const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility(); const groupsQuery = useQuery(groups(organizationId)); diff --git a/site/src/pages/StarterTemplatePage/StarterTemplatePage.tsx b/site/src/pages/StarterTemplatePage/StarterTemplatePage.tsx index 2278461b40b83..ed7b5b1c9d92f 100644 --- a/site/src/pages/StarterTemplatePage/StarterTemplatePage.tsx +++ b/site/src/pages/StarterTemplatePage/StarterTemplatePage.tsx @@ -3,13 +3,13 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { useParams } from "react-router-dom"; import { templateExamples } from "api/queries/templates"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { StarterTemplatePageView } from "./StarterTemplatePageView"; const StarterTemplatePage: FC = () => { const { exampleId } = useParams() as { exampleId: string }; - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const templateExamplesQuery = useQuery(templateExamples(organizationId)); const starterTemplate = templateExamplesQuery.data?.find( (example) => example.id === exampleId, diff --git a/site/src/pages/StarterTemplatesPage/StarterTemplatesPage.tsx b/site/src/pages/StarterTemplatesPage/StarterTemplatesPage.tsx index 74b31388f614c..d52c92a12df82 100644 --- a/site/src/pages/StarterTemplatesPage/StarterTemplatesPage.tsx +++ b/site/src/pages/StarterTemplatesPage/StarterTemplatesPage.tsx @@ -3,13 +3,13 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { templateExamples } from "api/queries/templates"; import type { TemplateExample } from "api/typesGenerated"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { getTemplatesByTag } from "utils/starterTemplates"; import { StarterTemplatesPageView } from "./StarterTemplatesPageView"; const StarterTemplatesPage: FC = () => { - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const templateExamplesQuery = useQuery(templateExamples(organizationId)); const starterTemplatesByTag = templateExamplesQuery.data ? // Currently, the scratch template should not be displayed on the starter templates page. diff --git a/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx b/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx index 9ee7161899d88..915241780c3fa 100644 --- a/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx +++ b/site/src/pages/TemplatePage/TemplateFilesPage/TemplateFilesPage.tsx @@ -3,13 +3,13 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { previousTemplateVersion, templateFiles } from "api/queries/templates"; import { Loader } from "components/Loader/Loader"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { TemplateFiles } from "modules/templates/TemplateFiles/TemplateFiles"; import { useTemplateLayoutContext } from "pages/TemplatePage/TemplateLayout"; import { getTemplatePageTitle } from "../utils"; const TemplateFilesPage: FC = () => { - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const { template, activeVersion } = useTemplateLayoutContext(); const { data: currentFiles } = useQuery( templateFiles(activeVersion.job.file_id), diff --git a/site/src/pages/TemplatePage/TemplateLayout.tsx b/site/src/pages/TemplatePage/TemplateLayout.tsx index e388c81feb27e..ec19d80c166cc 100644 --- a/site/src/pages/TemplatePage/TemplateLayout.tsx +++ b/site/src/pages/TemplatePage/TemplateLayout.tsx @@ -13,7 +13,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { TAB_PADDING_Y, TabLink, Tabs, TabsList } from "components/Tabs/Tabs"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { TemplatePageHeader } from "./TemplatePageHeader"; const templatePermissions = ( @@ -71,7 +71,7 @@ export const TemplateLayout: FC = ({ children = , }) => { const navigate = useNavigate(); - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const { template: templateName } = useParams() as { template: string }; const { data, error, isLoading } = useQuery({ queryKey: ["template", templateName], diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx index 4438cec0bea06..674505afd89e0 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx @@ -6,7 +6,6 @@ import { API } from "api/api"; import { templateByNameKey } from "api/queries/templates"; import type { UpdateTemplateMeta } from "api/typesGenerated"; import { displaySuccess } from "components/GlobalSnackbar/utils"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { useTemplateSettings } from "../TemplateSettingsLayout"; @@ -15,10 +14,9 @@ import { TemplateSettingsPageView } from "./TemplateSettingsPageView"; export const TemplateSettingsPage: FC = () => { const { template: templateName } = useParams() as { template: string }; const navigate = useNavigate(); - const { organizationId } = useAuthenticated(); const { template } = useTemplateSettings(); const queryClient = useQueryClient(); - const { entitlements } = useDashboard(); + const { entitlements, organizationId } = useDashboard(); const accessControlEnabled = entitlements.features.access_control.enabled; const advancedSchedulingEnabled = entitlements.features.advanced_template_scheduling.enabled; diff --git a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx index 79f0068147717..2e9aa072e699a 100644 --- a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx @@ -4,7 +4,7 @@ import { useMutation, useQuery, useQueryClient } from "react-query"; import { setGroupRole, setUserRole, templateACL } from "api/queries/templates"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { Paywall } from "components/Paywall/Paywall"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; import { docs } from "utils/docs"; import { pageTitle } from "utils/page"; @@ -12,7 +12,7 @@ import { useTemplateSettings } from "../TemplateSettingsLayout"; import { TemplatePermissionsPageView } from "./TemplatePermissionsPageView"; export const TemplatePermissionsPage: FC = () => { - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const { template, permissions } = useTemplateSettings(); const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility(); const templateACLQuery = useQuery(templateACL(template.id)); diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx index db37ed32dbcc3..45f87bdda5a5b 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx @@ -6,7 +6,6 @@ import { API } from "api/api"; import { templateByNameKey } from "api/queries/templates"; import type { UpdateTemplateMeta } from "api/typesGenerated"; import { displaySuccess } from "components/GlobalSnackbar/utils"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { useTemplateSettings } from "../TemplateSettingsLayout"; @@ -16,9 +15,8 @@ const TemplateSchedulePage: FC = () => { const { template: templateName } = useParams() as { template: string }; const navigate = useNavigate(); const queryClient = useQueryClient(); - const { organizationId } = useAuthenticated(); const { template } = useTemplateSettings(); - const { entitlements } = useDashboard(); + const { entitlements, organizationId } = useDashboard(); const allowAdvancedScheduling = entitlements.features["advanced_template_scheduling"].enabled; diff --git a/site/src/pages/TemplateSettingsPage/TemplateSettingsLayout.tsx b/site/src/pages/TemplateSettingsPage/TemplateSettingsLayout.tsx index 8e4bed6fb4b28..8e157dac3bd95 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSettingsLayout.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSettingsLayout.tsx @@ -9,7 +9,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert"; 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 { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { Sidebar } from "./Sidebar"; @@ -27,7 +27,7 @@ export function useTemplateSettings() { } export const TemplateSettingsLayout: FC = () => { - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const { template: templateName } = useParams() as { template: string }; const templateQuery = useQuery(templateByName(organizationId, templateName)); const permissionsQuery = useQuery({ diff --git a/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesPage.tsx b/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesPage.tsx index 94710daf4a98f..e717e24a2aab5 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesPage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesPage.tsx @@ -16,7 +16,7 @@ import type { 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 { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { useTemplateSettings } from "../TemplateSettingsLayout"; import { TemplateVariablesPageView } from "./TemplateVariablesPageView"; @@ -26,7 +26,7 @@ export const TemplateVariablesPage: FC = () => { organization: string; template: string; }; - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const { template } = useTemplateSettings(); const navigate = useNavigate(); const queryClient = useQueryClient(); diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 3a622630cd770..fa9d5e25be527 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -18,7 +18,7 @@ import type { } from "api/typesGenerated"; import { displayError } from "components/GlobalSnackbar/utils"; import { Loader } from "components/Loader/Loader"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { useWatchVersionLogs } from "modules/templates/useWatchVersionLogs"; import { type FileTree, traverse } from "utils/filetree"; import { pageTitle } from "utils/page"; @@ -36,7 +36,7 @@ export const TemplateVersionEditorPage: FC = () => { const navigate = useNavigate(); const { version: versionName, template: templateName } = useParams() as Params; - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); const templateQuery = useQuery(templateByName(organizationId, templateName)); const templateVersionOptions = templateVersionByName( organizationId, diff --git a/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx b/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx index 673fd5b5e91dc..dba108caa750b 100644 --- a/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx +++ b/site/src/pages/TemplateVersionPage/TemplateVersionPage.tsx @@ -9,6 +9,7 @@ import { templateVersionByName, } from "api/queries/templates"; import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import TemplateVersionPageView from "./TemplateVersionPageView"; @@ -20,7 +21,7 @@ type Params = { export const TemplateVersionPage: FC = () => { const { version: versionName, template: templateName } = useParams() as Params; - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); /** * Template version files diff --git a/site/src/pages/TemplatesPage/TemplatesPage.tsx b/site/src/pages/TemplatesPage/TemplatesPage.tsx index 019f32b1d7656..75c98d5221320 100644 --- a/site/src/pages/TemplatesPage/TemplatesPage.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPage.tsx @@ -3,11 +3,14 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { templateExamples, templates } from "api/queries/templates"; import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { TemplatesPageView } from "./TemplatesPageView"; export const TemplatesPage: FC = () => { - const { organizationId, permissions } = useAuthenticated(); + const { permissions } = useAuthenticated(); + const { organizationId } = useDashboard(); + const templatesQuery = useQuery(templates(organizationId)); const examplesQuery = useQuery({ ...templateExamples(organizationId), diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx index 38bac36ef6ed4..3a299e37b20aa 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx @@ -12,10 +12,10 @@ import { AccountForm } from "./AccountForm"; import { AccountUserGroups } from "./AccountUserGroups"; export const AccountPage: FC = () => { - const { user: me, permissions, organizationId } = useAuthenticated(); + const { permissions, user: me } = useAuthenticated(); const { updateProfile, updateProfileError, isUpdatingProfile } = useAuthContext(); - const { entitlements, experiments } = useDashboard(); + const { entitlements, experiments, organizationId } = useDashboard(); const hasGroupsFeature = entitlements.features.user_role_management.enabled; const groupsQuery = useQuery({ diff --git a/site/src/pages/UsersPage/UsersPage.tsx b/site/src/pages/UsersPage/UsersPage.tsx index be864a8634079..8ddc42e630aff 100644 --- a/site/src/pages/UsersPage/UsersPage.tsx +++ b/site/src/pages/UsersPage/UsersPage.tsx @@ -35,15 +35,13 @@ export const UsersPage: FC = () => { const navigate = useNavigate(); const searchParamsResult = useSearchParams(); - const { entitlements } = useDashboard(); + const { entitlements, organizationId } = useDashboard(); const [searchParams] = searchParamsResult; - const { organizationId } = useAuthenticated(); const groupsByUserIdQuery = useQuery(groupsByUserId(organizationId)); const authMethodsQuery = useQuery(authMethods()); - const { user: me } = useAuthenticated(); - const { permissions } = useAuthenticated(); + const { permissions, user: me } = useAuthenticated(); const { updateUsers: canEditUsers, viewDeploymentValues } = permissions; const rolesQuery = useQuery(roles()); const { data: deploymentValues } = useQuery({ diff --git a/site/src/pages/WorkspacePage/Workspace.stories.tsx b/site/src/pages/WorkspacePage/Workspace.stories.tsx index c321366862264..eb00430a8b30c 100644 --- a/site/src/pages/WorkspacePage/Workspace.stories.tsx +++ b/site/src/pages/WorkspacePage/Workspace.stories.tsx @@ -2,8 +2,8 @@ import { action } from "@storybook/addon-actions"; import type { Meta, StoryObj } from "@storybook/react"; import type { ProvisionerJobLog } from "api/typesGenerated"; import { ProxyContext, getPreferredProxy } from "contexts/ProxyContext"; -import { DashboardContext } from "modules/dashboard/DashboardProvider"; import * as Mocks from "testHelpers/entities"; +import { withDashboardProvider } from "testHelpers/storybook"; import type { WorkspacePermissions } from "./permissions"; import { Workspace } from "./Workspace"; import { WorkspaceBuildLogsSection } from "./WorkspaceBuildLogsSection"; @@ -32,35 +32,28 @@ const meta: Meta = { ], }, decorators: [ + withDashboardProvider, (Story) => ( - { + return; + }, + setProxy: () => { + return; + }, + refetchProxyLatencies: (): Date => { + return new Date(); + }, }} > - { - return; - }, - setProxy: () => { - return; - }, - refetchProxyLatencies: (): Date => { - return new Date(); - }, - }} - > - - - + + ), ], }; diff --git a/site/src/pages/WorkspacePage/WorkspacePage.tsx b/site/src/pages/WorkspacePage/WorkspacePage.tsx index 0331f5290bb73..11869d6254f82 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.tsx @@ -10,10 +10,10 @@ import type { Workspace } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; -import { useAuthenticated } from "contexts/auth/RequireAuth"; import { useEffectEvent } from "hooks/hookPolyfills"; import { Navbar } from "modules/dashboard/Navbar/Navbar"; import { NotificationBanners } from "modules/dashboard/NotificationBanners/NotificationBanners"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { workspaceChecks, type WorkspacePermissions } from "./permissions"; import { WorkspaceReadyPage } from "./WorkspaceReadyPage"; @@ -25,7 +25,7 @@ export const WorkspacePage: FC = () => { }; const workspaceName = params.workspace; const username = params.username.replace("@", ""); - const { organizationId } = useAuthenticated(); + const { organizationId } = useDashboard(); // Workspace const workspaceQueryOptions = workspaceByOwnerAndName( diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index bf959073aaeaa..277716f6a959c 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -38,8 +38,9 @@ const WorkspacesPage: FC = () => { // each hook. const searchParamsResult = useSafeSearchParams(); const pagination = usePagination({ searchParamsResult }); + const { permissions } = useAuthenticated(); + const { entitlements, organizationId } = useDashboard(); - const { organizationId, permissions } = useAuthenticated(); const templatesQuery = useQuery(templates(organizationId, false)); const filterProps = useWorkspacesFilter({ @@ -61,7 +62,6 @@ const WorkspacesPage: FC = () => { "delete" | "update" | null >(null); const [urlSearchParams] = searchParamsResult; - const { entitlements } = useDashboard(); const canCheckWorkspaces = entitlements.features["workspace_batch_actions"].enabled; const batchActions = useBatchActions({ diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx index 11fc39b142448..ac8b854c5a29d 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx @@ -12,18 +12,15 @@ import { getDefaultFilterProps, } from "components/Filter/storyHelpers"; import { DEFAULT_RECORDS_PER_PAGE } from "components/PaginationWidget/utils"; -import { DashboardContext } from "modules/dashboard/DashboardProvider"; import { MockWorkspace, - MockAppearanceConfig, MockBuildInfo, - MockEntitlementsWithScheduling, - MockExperiments, mockApiError, MockUser, MockPendingProvisionerJob, MockTemplate, } from "testHelpers/entities"; +import { withDashboardProvider } from "testHelpers/storybook"; import { WorkspacesPageView } from "./WorkspacesPageView"; const createWorkspace = ( @@ -141,19 +138,7 @@ const meta: Meta = { }, ], }, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [withDashboardProvider], }; export default meta; diff --git a/site/src/testHelpers/storybook.tsx b/site/src/testHelpers/storybook.tsx index 4d601e0dd67ef..77c0305d1aa2b 100644 --- a/site/src/testHelpers/storybook.tsx +++ b/site/src/testHelpers/storybook.tsx @@ -26,6 +26,8 @@ export const withDashboardProvider = ( return ( {}, entitlements, experiments, appearance: MockAppearanceConfig,