diff --git a/site/package.json b/site/package.json index 5989e0cae1047..ba14145a284a0 100644 --- a/site/package.json +++ b/site/package.json @@ -170,10 +170,9 @@ "vite-plugin-turbosnap": "1.0.2" }, "browserslist": [ - "chrome 66", - "firefox 63", - "edge 79", - "safari 15.4" + "chrome 110", + "firefox 111", + "safari 16.0" ], "resolutions": { "optionator": "0.9.3", diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx index 0ec595975f6b6..95b0f15e47f7b 100644 --- a/site/src/AppRouter.tsx +++ b/site/src/AppRouter.tsx @@ -5,8 +5,8 @@ import { BrowserRouter as Router, Navigate, } from "react-router-dom"; +import { DashboardLayout } from "./modules/dashboard/DashboardLayout"; import { RequireAuth } from "./contexts/auth/RequireAuth"; -import { DashboardLayout } from "./components/Dashboard/DashboardLayout"; import { FullScreenLoader } from "./components/Loader/FullScreenLoader"; import AuditPage from "./pages/AuditPage/AuditPage"; import { DeploySettingsLayout } from "./pages/DeploySettingsPage/DeploySettingsLayout"; diff --git a/site/src/components/WorkspaceStatusBadge/DormantDeletionText.tsx b/site/src/components/WorkspaceStatusBadge/DormantDeletionText.tsx index 924fc59cb47d9..3d5e5a3ed5bd4 100644 --- a/site/src/components/WorkspaceStatusBadge/DormantDeletionText.tsx +++ b/site/src/components/WorkspaceStatusBadge/DormantDeletionText.tsx @@ -1,7 +1,7 @@ import { type FC } from "react"; import type { Workspace } from "api/typesGenerated"; import { displayDormantDeletion } from "utils/dormant"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; interface DormantDeletionTextProps { workspace: Workspace; diff --git a/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx b/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx index 691532de06849..ec86e1099f7d8 100644 --- a/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx +++ b/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.stories.tsx @@ -15,7 +15,7 @@ import { MockAppearanceConfig, } from "testHelpers/entities"; import { WorkspaceStatusBadge } from "./WorkspaceStatusBadge"; -import { DashboardProviderContext } from "components/Dashboard/DashboardProvider"; +import { DashboardContext } from "modules/dashboard/DashboardProvider"; import type { Meta, StoryObj } from "@storybook/react"; const MockedAppearance = { @@ -29,7 +29,7 @@ const meta: Meta = { component: WorkspaceStatusBadge, decorators: [ (Story) => ( - = { }} > - + ), ], }; diff --git a/site/src/contexts/auth/RequireAuth.tsx b/site/src/contexts/auth/RequireAuth.tsx index c6ea2cc519a3c..55b78c2a8cfe7 100644 --- a/site/src/contexts/auth/RequireAuth.tsx +++ b/site/src/contexts/auth/RequireAuth.tsx @@ -4,7 +4,7 @@ import { Outlet, Navigate, useLocation } from "react-router-dom"; import { embedRedirect } from "utils/redirect"; import { isApiError } from "api/errors"; import { ProxyProvider } from "contexts/ProxyContext"; -import { DashboardProvider } from "components/Dashboard/DashboardProvider"; +import { DashboardProvider } from "modules/dashboard/DashboardProvider"; import { FullScreenLoader } from "components/Loader/FullScreenLoader"; import { useAuth } from "./useAuth"; diff --git a/site/src/hooks/index.ts b/site/src/hooks/index.ts index 40147df532412..852e4463f3d96 100644 --- a/site/src/hooks/index.ts +++ b/site/src/hooks/index.ts @@ -1,6 +1,5 @@ export * from "./useClickable"; export * from "./useClickableTableRow"; export * from "./useClipboard"; -export * from "./useFeatureVisibility"; export * from "./usePagination"; export * from "./useTab"; diff --git a/site/src/components/Dashboard/DashboardLayout.test.tsx b/site/src/modules/dashboard/DashboardLayout.test.tsx similarity index 100% rename from site/src/components/Dashboard/DashboardLayout.test.tsx rename to site/src/modules/dashboard/DashboardLayout.test.tsx diff --git a/site/src/components/Dashboard/DashboardLayout.tsx b/site/src/modules/dashboard/DashboardLayout.tsx similarity index 95% rename from site/src/components/Dashboard/DashboardLayout.tsx rename to site/src/modules/dashboard/DashboardLayout.tsx index af4cb9a0cb5e5..6bf30a22a3e1e 100644 --- a/site/src/components/Dashboard/DashboardLayout.tsx +++ b/site/src/modules/dashboard/DashboardLayout.tsx @@ -4,10 +4,10 @@ import Button from "@mui/material/Button"; import InfoOutlined from "@mui/icons-material/InfoOutlined"; import { type FC, type HTMLAttributes, Suspense } from "react"; import { Outlet } from "react-router-dom"; +import { LicenseBanner } from "modules/dashboard/LicenseBanner/LicenseBanner"; +import { ServiceBanner } from "modules/dashboard/ServiceBanner/ServiceBanner"; import { usePermissions } from "contexts/auth/usePermissions"; -import { LicenseBanner } from "components/Dashboard/LicenseBanner/LicenseBanner"; import { Loader } from "components/Loader/Loader"; -import { ServiceBanner } from "components/Dashboard/ServiceBanner/ServiceBanner"; import { dashboardContentBottomPadding } from "theme/constants"; import { docs } from "utils/docs"; import { Navbar } from "./Navbar/Navbar"; diff --git a/site/src/components/Dashboard/DashboardProvider.tsx b/site/src/modules/dashboard/DashboardProvider.tsx similarity index 81% rename from site/src/components/Dashboard/DashboardProvider.tsx rename to site/src/modules/dashboard/DashboardProvider.tsx index 7fcefb173eccf..fcd84f8d2d152 100644 --- a/site/src/components/Dashboard/DashboardProvider.tsx +++ b/site/src/modules/dashboard/DashboardProvider.tsx @@ -14,7 +14,6 @@ import { type PropsWithChildren, createContext, useCallback, - useContext, useState, } from "react"; import { appearance } from "api/queries/appearance"; @@ -27,16 +26,16 @@ interface Appearance { setPreview: (config: AppearanceConfig) => void; } -interface DashboardProviderValue { +export interface DashboardValue { buildInfo: BuildInfoResponse; entitlements: Entitlements; experiments: Experiments; appearance: Appearance; } -export const DashboardProviderContext = createContext< - DashboardProviderValue | undefined ->(undefined); +export const DashboardContext = createContext( + undefined, +); export const DashboardProvider: FC = ({ children }) => { const buildInfoQuery = useQuery(buildInfo()); @@ -83,7 +82,7 @@ export const DashboardProvider: FC = ({ children }) => { } return ( - = ({ children }) => { }} > {children} - + ); }; - -export const useDashboard = (): DashboardProviderValue => { - const context = useContext(DashboardProviderContext); - - if (!context) { - throw new Error( - "useDashboard only can be used inside of DashboardProvider", - ); - } - - return context; -}; - -export const useIsWorkspaceActionsEnabled = (): boolean => { - const { entitlements } = useDashboard(); - return entitlements.features["advanced_template_scheduling"].enabled; -}; diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBanner.tsx b/site/src/modules/dashboard/DeploymentBanner/DeploymentBanner.tsx similarity index 100% rename from site/src/components/Dashboard/DeploymentBanner/DeploymentBanner.tsx rename to site/src/modules/dashboard/DeploymentBanner/DeploymentBanner.tsx diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.stories.tsx b/site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.stories.tsx similarity index 100% rename from site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.stories.tsx rename to site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.stories.tsx diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx b/site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.tsx similarity index 100% rename from site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx rename to site/src/modules/dashboard/DeploymentBanner/DeploymentBannerView.tsx diff --git a/site/src/components/Dashboard/LicenseBanner/LicenseBanner.tsx b/site/src/modules/dashboard/LicenseBanner/LicenseBanner.tsx similarity index 83% rename from site/src/components/Dashboard/LicenseBanner/LicenseBanner.tsx rename to site/src/modules/dashboard/LicenseBanner/LicenseBanner.tsx index 6702c3c2bc8d4..1acba81f52043 100644 --- a/site/src/components/Dashboard/LicenseBanner/LicenseBanner.tsx +++ b/site/src/modules/dashboard/LicenseBanner/LicenseBanner.tsx @@ -1,5 +1,5 @@ import { type FC } from "react"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { LicenseBannerView } from "./LicenseBannerView"; export const LicenseBanner: FC = () => { diff --git a/site/src/components/Dashboard/LicenseBanner/LicenseBannerView.stories.tsx b/site/src/modules/dashboard/LicenseBanner/LicenseBannerView.stories.tsx similarity index 100% rename from site/src/components/Dashboard/LicenseBanner/LicenseBannerView.stories.tsx rename to site/src/modules/dashboard/LicenseBanner/LicenseBannerView.stories.tsx diff --git a/site/src/components/Dashboard/LicenseBanner/LicenseBannerView.tsx b/site/src/modules/dashboard/LicenseBanner/LicenseBannerView.tsx similarity index 100% rename from site/src/components/Dashboard/LicenseBanner/LicenseBannerView.tsx rename to site/src/modules/dashboard/LicenseBanner/LicenseBannerView.tsx diff --git a/site/src/components/Dashboard/Navbar/Navbar.test.tsx b/site/src/modules/dashboard/Navbar/Navbar.test.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/Navbar.test.tsx rename to site/src/modules/dashboard/Navbar/Navbar.test.tsx diff --git a/site/src/components/Dashboard/Navbar/Navbar.tsx b/site/src/modules/dashboard/Navbar/Navbar.tsx similarity index 90% rename from site/src/components/Dashboard/Navbar/Navbar.tsx rename to site/src/modules/dashboard/Navbar/Navbar.tsx index bd0f0705e73ef..3e618bbc876f8 100644 --- a/site/src/components/Dashboard/Navbar/Navbar.tsx +++ b/site/src/modules/dashboard/Navbar/Navbar.tsx @@ -3,8 +3,8 @@ import { useAuth } from "contexts/auth/useAuth"; import { useMe } from "contexts/auth/useMe"; import { usePermissions } from "contexts/auth/usePermissions"; import { useProxy } from "contexts/ProxyContext"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; -import { useFeatureVisibility } from "hooks/useFeatureVisibility"; +import { useDashboard } from "modules/dashboard/useDashboard"; +import { useFeatureVisibility } from "../useFeatureVisibility"; import { NavbarView } from "./NavbarView"; export const Navbar: FC = () => { diff --git a/site/src/components/Dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/NavbarView.stories.tsx rename to site/src/modules/dashboard/Navbar/NavbarView.stories.tsx diff --git a/site/src/components/Dashboard/Navbar/NavbarView.test.tsx b/site/src/modules/dashboard/Navbar/NavbarView.test.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/NavbarView.test.tsx rename to site/src/modules/dashboard/Navbar/NavbarView.test.tsx diff --git a/site/src/components/Dashboard/Navbar/NavbarView.tsx b/site/src/modules/dashboard/Navbar/NavbarView.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/NavbarView.tsx rename to site/src/modules/dashboard/Navbar/NavbarView.tsx diff --git a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx rename to site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.stories.tsx diff --git a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/UserDropdown/UserDropdown.tsx rename to site/src/modules/dashboard/Navbar/UserDropdown/UserDropdown.tsx diff --git a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx rename to site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.test.tsx diff --git a/site/src/components/Dashboard/Navbar/UserDropdown/UserDropdownContent.tsx b/site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx similarity index 100% rename from site/src/components/Dashboard/Navbar/UserDropdown/UserDropdownContent.tsx rename to site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx diff --git a/site/src/components/Dashboard/ServiceBanner/ServiceBanner.tsx b/site/src/modules/dashboard/ServiceBanner/ServiceBanner.tsx similarity index 87% rename from site/src/components/Dashboard/ServiceBanner/ServiceBanner.tsx rename to site/src/modules/dashboard/ServiceBanner/ServiceBanner.tsx index 1c03dbd88fcbc..94e350b89eb2c 100644 --- a/site/src/components/Dashboard/ServiceBanner/ServiceBanner.tsx +++ b/site/src/modules/dashboard/ServiceBanner/ServiceBanner.tsx @@ -1,5 +1,5 @@ import { type FC } from "react"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { ServiceBannerView } from "./ServiceBannerView"; export const ServiceBanner: FC = () => { diff --git a/site/src/components/Dashboard/ServiceBanner/ServiceBannerView.stories.tsx b/site/src/modules/dashboard/ServiceBanner/ServiceBannerView.stories.tsx similarity index 100% rename from site/src/components/Dashboard/ServiceBanner/ServiceBannerView.stories.tsx rename to site/src/modules/dashboard/ServiceBanner/ServiceBannerView.stories.tsx diff --git a/site/src/components/Dashboard/ServiceBanner/ServiceBannerView.tsx b/site/src/modules/dashboard/ServiceBanner/ServiceBannerView.tsx similarity index 100% rename from site/src/components/Dashboard/ServiceBanner/ServiceBannerView.tsx rename to site/src/modules/dashboard/ServiceBanner/ServiceBannerView.tsx diff --git a/site/src/modules/dashboard/useDashboard.ts b/site/src/modules/dashboard/useDashboard.ts new file mode 100644 index 0000000000000..9bb71613d2157 --- /dev/null +++ b/site/src/modules/dashboard/useDashboard.ts @@ -0,0 +1,14 @@ +import { useContext } from "react"; +import { DashboardContext, type DashboardValue } from "./DashboardProvider"; + +export const useDashboard = (): DashboardValue => { + const context = useContext(DashboardContext); + + if (!context) { + throw new Error( + "useDashboard only can be used inside of DashboardProvider", + ); + } + + return context; +}; diff --git a/site/src/hooks/useFeatureVisibility.ts b/site/src/modules/dashboard/useFeatureVisibility.ts similarity index 79% rename from site/src/hooks/useFeatureVisibility.ts rename to site/src/modules/dashboard/useFeatureVisibility.ts index 00e491a6e303d..a43111639e0cb 100644 --- a/site/src/hooks/useFeatureVisibility.ts +++ b/site/src/modules/dashboard/useFeatureVisibility.ts @@ -1,5 +1,5 @@ import { FeatureName } from "api/typesGenerated"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "./useDashboard"; import { selectFeatureVisibility } from "utils/entitlements"; export const useFeatureVisibility = (): Record => { diff --git a/site/src/components/Dashboard/useUpdateCheck.test.tsx b/site/src/modules/dashboard/useUpdateCheck.test.tsx similarity index 100% rename from site/src/components/Dashboard/useUpdateCheck.test.tsx rename to site/src/modules/dashboard/useUpdateCheck.test.tsx index b689f39d4252e..dec292346e57b 100644 --- a/site/src/components/Dashboard/useUpdateCheck.test.tsx +++ b/site/src/modules/dashboard/useUpdateCheck.test.tsx @@ -1,10 +1,10 @@ import { act, renderHook, waitFor } from "@testing-library/react"; -import { useUpdateCheck } from "./useUpdateCheck"; -import { QueryClient, QueryClientProvider } from "react-query"; import { type FC, type PropsWithChildren } from "react"; +import { QueryClient, QueryClientProvider } from "react-query"; import { rest } from "msw"; import { MockUpdateCheck } from "testHelpers/entities"; import { server } from "testHelpers/server"; +import { useUpdateCheck } from "./useUpdateCheck"; const createWrapper = (): FC => { const queryClient = new QueryClient(); diff --git a/site/src/components/Dashboard/useUpdateCheck.ts b/site/src/modules/dashboard/useUpdateCheck.ts similarity index 100% rename from site/src/components/Dashboard/useUpdateCheck.ts rename to site/src/modules/dashboard/useUpdateCheck.ts diff --git a/site/src/pages/AuditPage/AuditPage.tsx b/site/src/pages/AuditPage/AuditPage.tsx index 80f9cce869eb2..f9785379f4451 100644 --- a/site/src/pages/AuditPage/AuditPage.tsx +++ b/site/src/pages/AuditPage/AuditPage.tsx @@ -1,15 +1,15 @@ -import { isNonInitialPage } from "components/PaginationWidget/utils"; -import { useFeatureVisibility } from "hooks/useFeatureVisibility"; -import { FC } from "react"; +import { type FC } from "react"; import { Helmet } from "react-helmet-async"; import { useSearchParams } from "react-router-dom"; +import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; import { pageTitle } from "utils/page"; -import { AuditPageView } from "./AuditPageView"; +import { paginatedAudits } from "api/queries/audits"; +import { usePaginatedQuery } from "hooks/usePaginatedQuery"; import { useUserFilterMenu } from "components/Filter/UserFilter"; +import { isNonInitialPage } from "components/PaginationWidget/utils"; import { useFilter } from "components/Filter/filter"; import { useActionFilterMenu, useResourceTypeFilterMenu } from "./AuditFilter"; -import { usePaginatedQuery } from "hooks/usePaginatedQuery"; -import { paginatedAudits } from "api/queries/audits"; +import { AuditPageView } from "./AuditPageView"; const AuditPage: FC = () => { const { audit_log: isAuditLogVisible } = useFeatureVisibility(); diff --git a/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx b/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx index 8d3d70cb00cba..3a96ce6b9b571 100644 --- a/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx +++ b/site/src/pages/CreateTemplatePage/DuplicateTemplateView.tsx @@ -10,9 +10,9 @@ import { createTemplate, } from "api/queries/templates"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; import { CreateTemplateForm } from "./CreateTemplateForm"; import { firstVersionFromFile, getFormPermissions, newTemplate } from "./utils"; diff --git a/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx b/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx index 32c2308eded09..9ab04ab7044e8 100644 --- a/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx +++ b/site/src/pages/CreateTemplatePage/ImportStarterTemplateView.tsx @@ -9,9 +9,9 @@ import { templateVersionVariables, } from "api/queries/templates"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; import { CreateTemplateForm } from "./CreateTemplateForm"; import { firstVersionFromExample, diff --git a/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx b/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx index 5bd807d4d2267..e6f4e918ec3ac 100644 --- a/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx +++ b/site/src/pages/CreateTemplatePage/UploadTemplateView.tsx @@ -8,7 +8,7 @@ import { } from "api/queries/templates"; import { uploadFile } from "api/queries/files"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { CreateTemplateForm } from "./CreateTemplateForm"; import { firstVersionFromFile, getFormPermissions, newTemplate } from "./utils"; diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx index 439dc69f31763..fe5bd57fc42ca 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx @@ -1,13 +1,13 @@ -import { UpdateAppearanceConfig } from "api/typesGenerated"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; -import { FC } from "react"; +import { type FC } from "react"; import { Helmet } from "react-helmet-async"; -import { pageTitle } from "utils/page"; -import { AppearanceSettingsPageView } from "./AppearanceSettingsPageView"; import { useMutation, useQueryClient } from "react-query"; +import type { UpdateAppearanceConfig } from "api/typesGenerated"; +import { useDashboard } from "modules/dashboard/useDashboard"; +import { pageTitle } from "utils/page"; import { updateAppearance } from "api/queries/appearance"; import { getErrorMessage } from "api/errors"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; +import { AppearanceSettingsPageView } from "./AppearanceSettingsPageView"; // ServiceBanner is unlike the other Deployment Settings pages because it // implements a form, whereas the others are read-only. We make this diff --git a/site/src/pages/DeploySettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.tsx b/site/src/pages/DeploySettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.tsx index 6e2035f2f9107..5f2aeb2cf3ab3 100644 --- a/site/src/pages/DeploySettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.tsx @@ -1,8 +1,8 @@ +import { type FC } from "react"; +import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { getApps } from "api/queries/oauth2"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; -import { FC } from "react"; -import { Helmet } from "react-helmet-async"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import OAuth2AppsSettingsPageView from "./OAuth2AppsSettingsPageView"; diff --git a/site/src/pages/DeploySettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage.tsx b/site/src/pages/DeploySettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage.tsx index ece84c7e685bc..520e8a6b25060 100644 --- a/site/src/pages/DeploySettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage.tsx @@ -1,7 +1,7 @@ import { type FC } from "react"; import { Helmet } from "react-helmet-async"; import { pageTitle } from "utils/page"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { useDeploySettings } from "../DeploySettingsLayout"; import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView"; diff --git a/site/src/pages/DeploySettingsPage/SecuritySettingsPage/SecuritySettingsPage.tsx b/site/src/pages/DeploySettingsPage/SecuritySettingsPage/SecuritySettingsPage.tsx index 020af05f45a4f..e82f00a4b4a2e 100644 --- a/site/src/pages/DeploySettingsPage/SecuritySettingsPage/SecuritySettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/SecuritySettingsPage/SecuritySettingsPage.tsx @@ -1,7 +1,7 @@ import { type FC } from "react"; import { Helmet } from "react-helmet-async"; import { pageTitle } from "utils/page"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { SecuritySettingsPageView } from "./SecuritySettingsPageView"; import { useDeploySettings } from "../DeploySettingsLayout"; diff --git a/site/src/pages/GroupsPage/GroupsPage.tsx b/site/src/pages/GroupsPage/GroupsPage.tsx index 281c702e0c3ba..bb809ca1d2eaf 100644 --- a/site/src/pages/GroupsPage/GroupsPage.tsx +++ b/site/src/pages/GroupsPage/GroupsPage.tsx @@ -5,9 +5,9 @@ import { getErrorMessage } from "api/errors"; import { groups } from "api/queries/groups"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; import { usePermissions } from "contexts/auth/usePermissions"; -import { useFeatureVisibility } from "hooks/useFeatureVisibility"; -import { pageTitle } from "utils/page"; +import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; import { displayError } from "components/GlobalSnackbar/utils"; +import { pageTitle } from "utils/page"; import GroupsPageView from "./GroupsPageView"; export const GroupsPage: FC = () => { diff --git a/site/src/pages/HealthPage/AccessURLPage.stories.tsx b/site/src/pages/HealthPage/AccessURLPage.stories.tsx index 31ed1e32a2961..1fddaa03acb5a 100644 --- a/site/src/pages/HealthPage/AccessURLPage.stories.tsx +++ b/site/src/pages/HealthPage/AccessURLPage.stories.tsx @@ -13,4 +13,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as AccessURL }; diff --git a/site/src/pages/HealthPage/DERPPage.stories.tsx b/site/src/pages/HealthPage/DERPPage.stories.tsx index 8e87d3c03b2fb..194552361f568 100644 --- a/site/src/pages/HealthPage/DERPPage.stories.tsx +++ b/site/src/pages/HealthPage/DERPPage.stories.tsx @@ -13,4 +13,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as DERP }; diff --git a/site/src/pages/HealthPage/DERPRegionPage.stories.tsx b/site/src/pages/HealthPage/DERPRegionPage.stories.tsx index 6305c2eecc7b1..d462403be0c26 100644 --- a/site/src/pages/HealthPage/DERPRegionPage.stories.tsx +++ b/site/src/pages/HealthPage/DERPRegionPage.stories.tsx @@ -18,4 +18,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as DERPRegion }; diff --git a/site/src/pages/HealthPage/DatabasePage.stories.tsx b/site/src/pages/HealthPage/DatabasePage.stories.tsx index b250dbce68afe..f2ec5c5d0cf57 100644 --- a/site/src/pages/HealthPage/DatabasePage.stories.tsx +++ b/site/src/pages/HealthPage/DatabasePage.stories.tsx @@ -13,4 +13,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as Database }; diff --git a/site/src/pages/HealthPage/HealthLayout.tsx b/site/src/pages/HealthPage/HealthLayout.tsx index 0aef42e457991..df8b6fa166bca 100644 --- a/site/src/pages/HealthPage/HealthLayout.tsx +++ b/site/src/pages/HealthPage/HealthLayout.tsx @@ -1,24 +1,25 @@ -import { useMutation, useQuery, useQueryClient } from "react-query"; -import { Loader } from "components/Loader/Loader"; -import { Helmet } from "react-helmet-async"; -import { pageTitle } from "utils/page"; -import { createDayString } from "utils/createDayString"; -import { DashboardFullPage } from "components/Dashboard/DashboardLayout"; -import ReplayIcon from "@mui/icons-material/Replay"; -import { health, refreshHealth } from "api/queries/debug"; -import { useTheme } from "@mui/material/styles"; import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; import CircularProgress from "@mui/material/CircularProgress"; +import ReplayIcon from "@mui/icons-material/Replay"; +import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined"; +import { cx } from "@emotion/css"; +import { useTheme } from "@emotion/react"; +import { type FC, Suspense } from "react"; +import { Helmet } from "react-helmet-async"; +import { useMutation, useQuery, useQueryClient } from "react-query"; import { NavLink, Outlet } from "react-router-dom"; -import { css } from "@emotion/css"; import kebabCase from "lodash/fp/kebabCase"; -import { Suspense } from "react"; +import { health, refreshHealth } from "api/queries/debug"; +import type { HealthSeverity } from "api/typesGenerated"; +import { type ClassName, useClassName } from "hooks/useClassName"; +import { pageTitle } from "utils/page"; +import { createDayString } from "utils/createDayString"; +import { DashboardFullPage } from "modules/dashboard/DashboardLayout"; +import { Loader } from "components/Loader/Loader"; import { HealthIcon } from "./Content"; -import { HealthSeverity } from "api/typesGenerated"; -import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined"; -export function HealthLayout() { +export const HealthLayout: FC = () => { const theme = useTheme(); const queryClient = useQueryClient(); const { data: healthStatus } = useQuery({ @@ -38,6 +39,9 @@ export function HealthLayout() { } as const; const visibleSections = filterVisibleSections(sections); + const link = useClassName(classNames.link, []); + const activeLink = useClassName(classNames.activeLink, []); + return ( <> @@ -162,31 +166,7 @@ export function HealthLayout() { key={key} to={`/health/${kebabCase(key)}`} className={({ isActive }) => - css({ - background: isActive - ? theme.palette.action.hover - : "none", - pointerEvents: isActive ? "none" : "auto", - color: isActive - ? theme.palette.text.primary - : theme.palette.text.secondary, - border: "none", - fontSize: 14, - width: "100%", - display: "flex", - alignItems: "center", - gap: 12, - textAlign: "left", - height: 36, - padding: "0 24px", - cursor: "pointer", - textDecoration: "none", - - "&:hover": { - background: theme.palette.action.hover, - color: theme.palette.text.primary, - }, - }) + cx([link, isActive && activeLink]) } > ); -} +}; const filterVisibleSections = (sections: T) => { return Object.keys(sections).reduce( @@ -240,3 +220,35 @@ const filterVisibleSections = (sections: T) => { {} as Partial, ); }; + +const classNames = { + link: (css, theme) => + css({ + background: "none", + pointerEvents: "auto", + color: theme.palette.text.secondary, + border: "none", + fontSize: 14, + width: "100%", + display: "flex", + alignItems: "center", + gap: 12, + textAlign: "left", + height: 36, + padding: "0 24px", + cursor: "pointer", + textDecoration: "none", + + "&:hover": { + background: theme.palette.action.hover, + color: theme.palette.text.primary, + }, + }), + + activeLink: (css, theme) => + css({ + background: theme.palette.action.hover, + pointerEvents: "none", + color: theme.palette.text.primary, + }), +} satisfies Record; diff --git a/site/src/pages/HealthPage/ProvisionerDaemonsPage.stories.tsx b/site/src/pages/HealthPage/ProvisionerDaemonsPage.stories.tsx index 7117aa886967e..b80a20c1ed159 100644 --- a/site/src/pages/HealthPage/ProvisionerDaemonsPage.stories.tsx +++ b/site/src/pages/HealthPage/ProvisionerDaemonsPage.stories.tsx @@ -13,4 +13,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as ProvisionerDaemons }; diff --git a/site/src/pages/HealthPage/WebsocketPage.stories.tsx b/site/src/pages/HealthPage/WebsocketPage.stories.tsx index 896bc7126ca95..4eba44bc48b58 100644 --- a/site/src/pages/HealthPage/WebsocketPage.stories.tsx +++ b/site/src/pages/HealthPage/WebsocketPage.stories.tsx @@ -13,4 +13,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as Websocket }; diff --git a/site/src/pages/HealthPage/WorkspaceProxyPage.stories.tsx b/site/src/pages/HealthPage/WorkspaceProxyPage.stories.tsx index 446bf1ea6f9f0..92e031014079a 100644 --- a/site/src/pages/HealthPage/WorkspaceProxyPage.stories.tsx +++ b/site/src/pages/HealthPage/WorkspaceProxyPage.stories.tsx @@ -13,4 +13,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +const Example: Story = {}; + +export { Example as WorkspaceProxy }; diff --git a/site/src/pages/HealthPage/storybook.tsx b/site/src/pages/HealthPage/storybook.tsx index 886402af3a1bf..776e5f3e8fb03 100644 --- a/site/src/pages/HealthPage/storybook.tsx +++ b/site/src/pages/HealthPage/storybook.tsx @@ -14,7 +14,7 @@ import { MockHealthSettings, } from "testHelpers/entities"; import { HEALTH_QUERY_KEY, HEALTH_QUERY_SETTINGS_KEY } from "api/queries/debug"; -import { DashboardProvider } from "components/Dashboard/DashboardProvider"; +import { DashboardProvider } from "modules/dashboard/DashboardProvider"; import { HealthLayout } from "./HealthLayout"; type MetaOptions = { @@ -25,7 +25,7 @@ type MetaOptions = { export const generateMeta = ({ element, path, params }: MetaOptions): Meta => { return { - render: HealthLayout, + component: HealthLayout, parameters: { chromatic, layout: "fullscreen", diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx index adc760e4bcebc..572b788aacbab 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.tsx @@ -2,12 +2,12 @@ import { type FC } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQueryClient } from "react-query"; import { useNavigate, useParams } from "react-router-dom"; -import { pageTitle } from "utils/page"; import { updateTemplateMeta } from "api/api"; import type { UpdateTemplateMeta } from "api/typesGenerated"; import { templateByNameKey } from "api/queries/templates"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; +import { pageTitle } from "utils/page"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { useTemplateSettings } from "../TemplateSettingsLayout"; import { TemplateSettingsPageView } from "./TemplateSettingsPageView"; diff --git a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx index 264259270e293..f7173ff0d67cf 100644 --- a/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplatePermissionsPage/TemplatePermissionsPage.tsx @@ -8,16 +8,14 @@ import { setGroupRole, setUserRole, templateACL } from "api/queries/templates"; import { Paywall } from "components/Paywall/Paywall"; import { Stack } from "components/Stack/Stack"; import { displaySuccess } from "components/GlobalSnackbar/utils"; -import { useFeatureVisibility } from "hooks/useFeatureVisibility"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; +import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; import { pageTitle } from "utils/page"; import { docs } from "utils/docs"; import { useTemplateSettings } from "../TemplateSettingsLayout"; import { TemplatePermissionsPageView } from "./TemplatePermissionsPageView"; -export const TemplatePermissionsPage: FC< - React.PropsWithChildren -> = () => { +export const TemplatePermissionsPage: FC = () => { const organizationId = useOrganizationId(); const { template, permissions } = useTemplateSettings(); const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility(); diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx index 8e75e29ae860a..5c8cff6184241 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx @@ -2,12 +2,12 @@ import { type FC } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQueryClient } from "react-query"; import { useNavigate, useParams } from "react-router-dom"; -import { pageTitle } from "utils/page"; import { updateTemplateMeta } from "api/api"; import type { UpdateTemplateMeta } from "api/typesGenerated"; import { templateByNameKey } from "api/queries/templates"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; +import { pageTitle } from "utils/page"; import { displaySuccess } from "components/GlobalSnackbar/utils"; import { useTemplateSettings } from "../TemplateSettingsLayout"; import { TemplateSchedulePageView } from "./TemplateSchedulePageView"; diff --git a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx index c2e7512a29630..c2f11435806ea 100644 --- a/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx +++ b/site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx @@ -5,7 +5,7 @@ import { useAuth } from "contexts/auth/useAuth"; import { useMe } from "contexts/auth/useMe"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; import { usePermissions } from "contexts/auth/usePermissions"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { Stack } from "components/Stack/Stack"; import { Section } from "../Section"; import { AccountUserGroups } from "./AccountUserGroups"; diff --git a/site/src/pages/UserSettingsPage/Sidebar.tsx b/site/src/pages/UserSettingsPage/Sidebar.tsx index a67d7b2bed171..ddad8a2ab5468 100644 --- a/site/src/pages/UserSettingsPage/Sidebar.tsx +++ b/site/src/pages/UserSettingsPage/Sidebar.tsx @@ -13,7 +13,7 @@ import { SidebarNavItem, } from "components/Sidebar/Sidebar"; import { GitIcon } from "components/Icons/GitIcon"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; interface SidebarProps { user: User; diff --git a/site/src/pages/UsersPage/UsersLayout.tsx b/site/src/pages/UsersPage/UsersLayout.tsx index e2a7c00e94bc6..2f8a72d5840fe 100644 --- a/site/src/pages/UsersPage/UsersLayout.tsx +++ b/site/src/pages/UsersPage/UsersLayout.tsx @@ -4,10 +4,10 @@ import GroupAdd from "@mui/icons-material/GroupAddOutlined"; import PersonAdd from "@mui/icons-material/PersonAddOutlined"; import { type FC, Suspense } from "react"; import { Link as RouterLink, Outlet, useNavigate } from "react-router-dom"; -import { USERS_LINK } from "components/Dashboard/Navbar/NavbarView"; -import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; -import { useFeatureVisibility } from "hooks/useFeatureVisibility"; import { usePermissions } from "contexts/auth/usePermissions"; +import { USERS_LINK } from "modules/dashboard/Navbar/NavbarView"; +import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; +import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader"; import { Margins } from "components/Margins/Margins"; import { TabLink, Tabs } from "components/Tabs/Tabs"; import { Loader } from "components/Loader/Loader"; diff --git a/site/src/pages/UsersPage/UsersPage.tsx b/site/src/pages/UsersPage/UsersPage.tsx index 165254a359a65..e6547824fcc4d 100644 --- a/site/src/pages/UsersPage/UsersPage.tsx +++ b/site/src/pages/UsersPage/UsersPage.tsx @@ -2,7 +2,7 @@ import { type FC, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { useSearchParams, useNavigate } from "react-router-dom"; -import { type User } from "api/typesGenerated"; +import type { User } from "api/typesGenerated"; import { roles } from "api/queries/roles"; import { groupsByUserId } from "api/queries/groups"; import { getErrorMessage } from "api/errors"; @@ -17,10 +17,10 @@ import { authMethods, } from "api/queries/users"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; -import { useMe } from "contexts/auth/useMe"; import { usePermissions } from "contexts/auth/usePermissions"; +import { useMe } from "contexts/auth/useMe"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { useFilter } from "components/Filter/filter"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; diff --git a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx index ed61339d7bee4..ec26222a08921 100644 --- a/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx +++ b/site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx @@ -1,7 +1,10 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react"; -import { BuildAvatar } from "components/BuildAvatar/BuildAvatar"; import { type FC } from "react"; -import { ProvisionerJobLog, WorkspaceBuild } from "api/typesGenerated"; +import type { ProvisionerJobLog, WorkspaceBuild } from "api/typesGenerated"; +import { Link } from "react-router-dom"; +import { displayWorkspaceBuildDuration } from "utils/workspace"; +import { DashboardFullPage } from "modules/dashboard/DashboardLayout"; +import { BuildAvatar } from "components/BuildAvatar/BuildAvatar"; import { Loader } from "components/Loader/Loader"; import { Stack } from "components/Stack/Stack"; import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs"; @@ -10,16 +13,13 @@ import { PageHeaderTitle, PageHeaderSubtitle, } from "components/PageHeader/FullWidthPageHeader"; -import { Link } from "react-router-dom"; import { Stats, StatsItem } from "components/Stats/Stats"; -import { displayWorkspaceBuildDuration } from "utils/workspace"; -import { Sidebar, SidebarCaption, SidebarItem } from "./Sidebar"; import { Alert } from "components/Alert/Alert"; -import { DashboardFullPage } from "components/Dashboard/DashboardLayout"; import { WorkspaceBuildData, WorkspaceBuildDataSkeleton, } from "components/WorkspaceBuild/WorkspaceBuildData"; +import { Sidebar, SidebarCaption, SidebarItem } from "./Sidebar"; const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => { return [...logs].sort( diff --git a/site/src/pages/WorkspacePage/Workspace.stories.tsx b/site/src/pages/WorkspacePage/Workspace.stories.tsx index 07a320c56c513..9c9b41dbf7416 100644 --- a/site/src/pages/WorkspacePage/Workspace.stories.tsx +++ b/site/src/pages/WorkspacePage/Workspace.stories.tsx @@ -1,14 +1,14 @@ import { action } from "@storybook/addon-actions"; import { Meta, StoryObj } from "@storybook/react"; -import { WatchAgentMetadataContext } from "components/Resources/AgentMetadata"; -import { ProvisionerJobLog } from "api/typesGenerated"; -import * as Mocks from "testHelpers/entities"; -import { Workspace } from "./Workspace"; -import { withReactContext } from "storybook-react-context"; import EventSource from "eventsourcemock"; +import { withReactContext } from "storybook-react-context"; +import type { ProvisionerJobLog } from "api/typesGenerated"; +import * as Mocks from "testHelpers/entities"; import { ProxyContext, getPreferredProxy } from "contexts/ProxyContext"; -import { DashboardProviderContext } from "components/Dashboard/DashboardProvider"; -import { WorkspaceBuildLogsSection } from "pages/WorkspacePage/WorkspaceBuildLogsSection"; +import { DashboardContext } from "modules/dashboard/DashboardProvider"; +import { WatchAgentMetadataContext } from "components/Resources/AgentMetadata"; +import { Workspace } from "./Workspace"; +import { WorkspaceBuildLogsSection } from "./WorkspaceBuildLogsSection"; import { WorkspacePermissions } from "./permissions"; const MockedAppearance = { @@ -38,7 +38,7 @@ const meta: Meta = { }, decorators: [ (Story) => ( - = { > - + ), withReactContext({ Context: WatchAgentMetadataContext, diff --git a/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx b/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx index 811ce5214bfff..da5af95863070 100644 --- a/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx @@ -1,20 +1,20 @@ import { workspaceResolveAutostart } from "api/queries/workspaceQuota"; import { Template, TemplateVersion, Workspace } from "api/typesGenerated"; -import { FC, useEffect, useState } from "react"; +import { type Interpolation, type Theme } from "@emotion/react"; +import { type FC, useEffect, useState } from "react"; import { useQuery } from "react-query"; -import { WorkspacePermissions } from "../permissions"; import dayjs from "dayjs"; -import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import formatDistanceToNow from "date-fns/formatDistanceToNow"; import InfoOutlined from "@mui/icons-material/InfoOutlined"; import WarningRounded from "@mui/icons-material/WarningRounded"; import { MemoizedInlineMarkdown } from "components/Markdown/Markdown"; +import type { WorkspacePermissions } from "../permissions"; import { NotificationActionButton, NotificationItem, Notifications, } from "./Notifications"; -import { Interpolation, Theme } from "@emotion/react"; type WorkspaceNotificationsProps = { workspace: Workspace; @@ -103,8 +103,10 @@ export const WorkspaceNotifications: FC = ({ } // Dormant - const areActionsEnabled = useIsWorkspaceActionsEnabled(); - if (areActionsEnabled && workspace.dormant_at) { + const { entitlements } = useDashboard(); + const advancedSchedulingEnabled = + entitlements.features["advanced_template_scheduling"].enabled; + if (advancedSchedulingEnabled && workspace.dormant_at) { const formatDate = (dateStr: string, timestamp: boolean): string => { const date = new Date(dateStr); return date.toLocaleDateString(undefined, { diff --git a/site/src/pages/WorkspacePage/WorkspacePage.tsx b/site/src/pages/WorkspacePage/WorkspacePage.tsx index c70f83de6316f..b5aa890021f79 100644 --- a/site/src/pages/WorkspacePage/WorkspacePage.tsx +++ b/site/src/pages/WorkspacePage/WorkspacePage.tsx @@ -1,18 +1,18 @@ import { type FC, useEffect } from "react"; import { useQuery, useQueryClient } from "react-query"; import { useParams } from "react-router-dom"; -import { useEffectEvent } from "hooks/hookPolyfills"; -import { useOrganizationId } from "contexts/auth/useOrganizationId"; import { watchWorkspace } from "api/api"; import type { Workspace } from "api/typesGenerated"; import { workspaceBuildsKey } from "api/queries/workspaceBuilds"; import { templateByName } from "api/queries/templates"; import { workspaceByOwnerAndName } from "api/queries/workspaces"; import { checkAuthorization } from "api/queries/authCheck"; +import { useEffectEvent } from "hooks/hookPolyfills"; +import { useOrganizationId } from "contexts/auth/useOrganizationId"; +import { Navbar } from "modules/dashboard/Navbar/Navbar"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; -import { Navbar } from "components/Dashboard/Navbar/Navbar"; import { WorkspacePermissions, workspaceChecks } from "./permissions"; import { WorkspaceReadyPage } from "./WorkspaceReadyPage"; diff --git a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx index a39876e1bbcfe..10441590784f7 100644 --- a/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx @@ -4,21 +4,15 @@ import { Helmet } from "react-helmet-async"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { useNavigate } from "react-router-dom"; import { MissingBuildParameters, restartWorkspace } from "api/api"; -import { useFeatureVisibility } from "hooks/useFeatureVisibility"; -import { pageTitle } from "utils/page"; import { getErrorMessage } from "api/errors"; import type * as TypesGen from "api/typesGenerated"; import { templateVersion, templateVersions } from "api/queries/templates"; import { deploymentConfig, deploymentSSHConfig } from "api/queries/deployment"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; -import { - ConfirmDialog, - ConfirmDialogProps, -} from "components/Dialogs/ConfirmDialog/ConfirmDialog"; -import { Alert } from "components/Alert/Alert"; -import { Stack } from "components/Stack/Stack"; import { useMe } from "contexts/auth/useMe"; import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs"; +import { useDashboard } from "modules/dashboard/useDashboard"; +import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility"; +import { pageTitle } from "utils/page"; import { activate, changeVersion, @@ -28,9 +22,15 @@ import { startWorkspace, cancelBuild, } from "api/queries/workspaces"; +import { Alert } from "components/Alert/Alert"; +import { Stack } from "components/Stack/Stack"; +import { + ConfirmDialog, + ConfirmDialogProps, +} from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import { displayError } from "components/GlobalSnackbar/utils"; -import { WorkspacePermissions } from "./permissions"; import { ChangeVersionDialog } from "./ChangeVersionDialog"; +import { WorkspacePermissions } from "./permissions"; import { UpdateBuildParametersDialog } from "./UpdateBuildParametersDialog"; import { Workspace } from "./Workspace"; import { WorkspaceBuildLogsSection } from "./WorkspaceBuildLogsSection"; diff --git a/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx b/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx index 96bf6ada94ab4..88e2634ef4418 100644 --- a/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx @@ -1,6 +1,15 @@ +import Tooltip from "@mui/material/Tooltip"; +import Link from "@mui/material/Link"; +import MonetizationOnOutlined from "@mui/icons-material/MonetizationOnOutlined"; +import DeleteOutline from "@mui/icons-material/DeleteOutline"; +import PersonOutline from "@mui/icons-material/PersonOutline"; +import ArrowBackOutlined from "@mui/icons-material/ArrowBackOutlined"; +import ScheduleOutlined from "@mui/icons-material/ScheduleOutlined"; +import { useTheme } from "@emotion/react"; +import { type FC } from "react"; +import { useQuery } from "react-query"; import { Link as RouterLink } from "react-router-dom"; import type * as TypesGen from "api/typesGenerated"; -import { WorkspaceActions } from "pages/WorkspacePage/WorkspaceActions/WorkspaceActions"; import { Topbar, TopbarAvatar, @@ -9,27 +18,19 @@ import { TopbarIcon, TopbarIconButton, } from "components/FullPageLayout/Topbar"; -import Tooltip from "@mui/material/Tooltip"; -import ArrowBackOutlined from "@mui/icons-material/ArrowBackOutlined"; -import ScheduleOutlined from "@mui/icons-material/ScheduleOutlined"; import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"; import { WorkspaceScheduleControls, shouldDisplayScheduleControls, } from "./WorkspaceScheduleControls"; import { workspaceQuota } from "api/queries/workspaceQuota"; -import { useQuery } from "react-query"; -import MonetizationOnOutlined from "@mui/icons-material/MonetizationOnOutlined"; -import { useTheme } from "@mui/material/styles"; -import Link from "@mui/material/Link"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { displayDormantDeletion } from "utils/dormant"; -import DeleteOutline from "@mui/icons-material/DeleteOutline"; -import PersonOutline from "@mui/icons-material/PersonOutline"; import { Popover, PopoverTrigger } from "components/Popover/Popover"; import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip"; import { AvatarData } from "components/AvatarData/AvatarData"; import { ExternalAvatar } from "components/Avatar/Avatar"; +import { WorkspaceActions } from "./WorkspaceActions/WorkspaceActions"; import { WorkspaceNotifications } from "./WorkspaceNotifications/WorkspaceNotifications"; import { WorkspacePermissions } from "./permissions"; @@ -64,30 +65,29 @@ export interface WorkspaceProps { latestVersion?: TypesGen.TemplateVersion; } -export const WorkspaceTopbar = (props: WorkspaceProps) => { - const { - handleStart, - handleStop, - handleRestart, - handleDelete, - handleUpdate, - handleCancel, - handleSettings, - handleChangeVersion, - handleDormantActivate, - workspace, - isUpdating, - isRestarting, - canUpdateWorkspace, - canChangeVersions, - canRetryDebugMode, - handleBuildRetry, - handleBuildRetryDebug, - isOwner, - template, - latestVersion, - permissions, - } = props; +export const WorkspaceTopbar: FC = ({ + handleStart, + handleStop, + handleRestart, + handleDelete, + handleUpdate, + handleCancel, + handleSettings, + handleChangeVersion, + handleDormantActivate, + workspace, + isUpdating, + isRestarting, + canUpdateWorkspace, + canChangeVersions, + canRetryDebugMode, + handleBuildRetry, + handleBuildRetryDebug, + isOwner, + template, + latestVersion, + permissions, +}) => { const theme = useTheme(); // Quota diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index 077e85c6f80fb..3fba54f8ad951 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -2,16 +2,16 @@ import { type FC, useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { useSearchParams } from "react-router-dom"; -import { templates } from "api/queries/templates"; import type { Workspace } from "api/typesGenerated"; +import { templates } from "api/queries/templates"; import { useOrganizationId } from "contexts/auth/useOrganizationId"; import { usePermissions } from "contexts/auth/usePermissions"; +import { useEffectEvent } from "hooks/hookPolyfills"; import { usePagination } from "hooks/usePagination"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; -import { useDashboard } from "components/Dashboard/DashboardProvider"; import { useFilter } from "components/Filter/filter"; import { useUserFilterMenu } from "components/Filter/UserFilter"; -import { useEffectEvent } from "hooks/hookPolyfills"; import { useBatchActions } from "./batchActions"; import { useWorkspacesData, useWorkspaceUpdate } from "./data"; import { useTemplateFilterMenu, useStatusFilterMenu } from "./filter/menus"; diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx index 1b0bf8c814ecc..cfff23823889e 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.stories.tsx @@ -19,7 +19,7 @@ import { MockTemplate, } from "testHelpers/entities"; import { WorkspacesPageView } from "./WorkspacesPageView"; -import { DashboardProviderContext } from "components/Dashboard/DashboardProvider"; +import { DashboardContext } from "modules/dashboard/DashboardProvider"; import { ComponentProps } from "react"; import { MockMenu, @@ -141,7 +141,7 @@ const meta: Meta = { }, decorators: [ (Story) => ( - = { }} > - + ), ], }; diff --git a/site/src/pages/WorkspacesPage/filter/filter.tsx b/site/src/pages/WorkspacesPage/filter/filter.tsx index f89e2992c4a6d..112aac6f18e0d 100644 --- a/site/src/pages/WorkspacesPage/filter/filter.tsx +++ b/site/src/pages/WorkspacesPage/filter/filter.tsx @@ -1,9 +1,7 @@ -import { type FC } from "react"; import { useTheme } from "@emotion/react"; -import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider"; +import { type FC } from "react"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { Avatar, type AvatarProps } from "components/Avatar/Avatar"; -import type { TemplateFilterMenu, StatusFilterMenu } from "./menus"; -import type { TemplateOption, StatusOption } from "./options"; import { Filter, FilterMenu, @@ -15,6 +13,8 @@ import { } from "components/Filter/filter"; import { type UserFilterMenu, UserMenu } from "components/Filter/UserFilter"; import { docs } from "utils/docs"; +import type { TemplateFilterMenu, StatusFilterMenu } from "./menus"; +import type { TemplateOption, StatusOption } from "./options"; export const workspaceFilterQuery = { me: "owner:me", @@ -74,8 +74,10 @@ export const WorkspacesFilter: FC = ({ error, menus, }) => { - const actionsEnabled = useIsWorkspaceActionsEnabled(); - const presets = actionsEnabled ? PRESETS_WITH_DORMANT : PRESET_FILTERS; + const { entitlements } = useDashboard(); + const presets = entitlements.features["advanced_template_scheduling"].enabled + ? PRESETS_WITH_DORMANT + : PRESET_FILTERS; return ( { - acc[feature] = { enabled: true, entitlement: "entitled" }; - return acc; - }, - {} as Entitlements["features"], + Object.fromEntries( + features.map((feature) => [ + feature, + { enabled: true, entitlement: "entitled" }, + ]), ), ), }; return ( - - + ); }; diff --git a/site/vite.config.ts b/site/vite.config.ts index 82d32c63a655f..1ca3de51cb292 100644 --- a/site/vite.config.ts +++ b/site/vite.config.ts @@ -84,14 +84,13 @@ export default defineConfig({ alias: { api: path.resolve(__dirname, "./src/api"), components: path.resolve(__dirname, "./src/components"), - hooks: path.resolve(__dirname, "./src/hooks"), contexts: path.resolve(__dirname, "./src/contexts"), - i18n: path.resolve(__dirname, "./src/i18n"), + hooks: path.resolve(__dirname, "./src/hooks"), + modules: path.resolve(__dirname, "./src/modules"), pages: path.resolve(__dirname, "./src/pages"), testHelpers: path.resolve(__dirname, "./src/testHelpers"), theme: path.resolve(__dirname, "./src/theme"), utils: path.resolve(__dirname, "./src/utils"), - xServices: path.resolve(__dirname, "./src/xServices"), }, }, });