chore: consolidate ManageSettingsLayout code (#14885)

Clean up a bunch of tangles that only existed to service the
`"multi-organization"` experiment, which has now been removed
This commit is contained in:
Kayla Washburn-Love
2024-10-03 22:00:52 +00:00
committed by GitHub
parent 04af56d54b
commit 8b6a06dbd5
78 changed files with 237 additions and 335 deletions
@@ -5,7 +5,9 @@ import { beforeCoderTest } from "../../hooks";
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
test("create user with password", async ({ page, baseURL }) => {
await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
await page.goto(`${baseURL}/deployment/users`, {
waitUntil: "domcontentloaded",
});
await expect(page).toHaveTitle("Users - Coder");
await page.getByRole("button", { name: "Create user" }).click();
@@ -37,7 +39,9 @@ test("create user with password", async ({ page, baseURL }) => {
});
test("create user without full name is optional", async ({ page, baseURL }) => {
await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
await page.goto(`${baseURL}/deployment/users`, {
waitUntil: "domcontentloaded",
});
await expect(page).toHaveTitle("Users - Coder");
await page.getByRole("button", { name: "Create user" }).click();
@@ -114,7 +114,7 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
{canViewAllUsers && (
<MenuItem
component={NavLink}
to={canViewOrganizations ? `/deployment${linkToUsers}` : linkToUsers}
to={linkToUsers}
css={styles.menuItem}
onClick={onPopoverClose}
>
@@ -1,24 +1,37 @@
import type { DeploymentConfig } from "api/api";
import { deploymentConfig } from "api/queries/deployment";
import type { AuthorizationResponse, Organization } from "api/typesGenerated";
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 { RequirePermission } from "contexts/auth/RequirePermission";
import { useDashboard } from "modules/dashboard/useDashboard";
import { type FC, Suspense } from "react";
import { type FC, Suspense, createContext, useContext } from "react";
import { useQuery } from "react-query";
import { Outlet } from "react-router-dom";
import { DeploySettingsContext } from "../DeploySettingsPage/DeploySettingsLayout";
import { Outlet, useParams } from "react-router-dom";
import { Sidebar } from "./Sidebar";
type OrganizationSettingsValue = Readonly<{
export const ManagementSettingsContext = createContext<
ManagementSettingsValue | undefined
>(undefined);
type ManagementSettingsValue = Readonly<{
deploymentValues: DeploymentConfig;
organizations: readonly Organization[];
organization?: Organization;
}>;
export const useOrganizationSettings = (): OrganizationSettingsValue => {
const { organizations } = useDashboard();
return { organizations };
export const useManagementSettings = (): ManagementSettingsValue => {
const context = useContext(ManagementSettingsContext);
if (!context) {
throw new Error(
"useManagementSettings should be used inside of ManagementSettingsLayout",
);
}
return context;
};
/**
@@ -43,13 +56,11 @@ export const canEditOrganization = (
*/
export const ManagementSettingsLayout: FC = () => {
const { permissions } = useAuthenticated();
const deploymentConfigQuery = useQuery(
// TODO: This is probably normally fine because we will not show links to
// pages that need this data, but if you manually visit the page you
// will see an endless loader when maybe we should show a "permission
// denied" error or at least a 404 instead.
permissions.viewDeploymentValues ? deploymentConfig() : { enabled: false },
);
const deploymentConfigQuery = useQuery(deploymentConfig());
const { organizations } = useDashboard();
const { organization: orgName } = useParams() as {
organization?: string;
};
// The deployment settings page also contains users, audit logs, groups and
// organizations, so this page must be visible if you can see any of these.
@@ -59,24 +70,39 @@ export const ManagementSettingsLayout: FC = () => {
permissions.editAnyOrganization ||
permissions.viewAnyAuditLog;
if (deploymentConfigQuery.error) {
return <ErrorAlert error={deploymentConfigQuery.error} />;
}
if (!deploymentConfigQuery.data) {
return <Loader />;
}
const organization =
organizations && orgName
? organizations.find((org) => org.name === orgName)
: undefined;
return (
<RequirePermission isFeatureVisible={canViewDeploymentSettingsPage}>
<Margins>
<Stack css={{ padding: "48px 0" }} direction="row" spacing={6}>
<Sidebar />
<main css={{ width: "100%" }}>
<DeploySettingsContext.Provider
value={{
deploymentValues: deploymentConfigQuery.data,
}}
>
<ManagementSettingsContext.Provider
value={{
deploymentValues: deploymentConfigQuery.data,
organizations,
organization,
}}
>
<Margins>
<Stack css={{ padding: "48px 0" }} direction="row" spacing={6}>
<Sidebar />
<main css={{ width: "100%" }}>
<Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
</DeploySettingsContext.Provider>
</main>
</Stack>
</Margins>
</main>
</Stack>
</Margins>
</ManagementSettingsContext.Provider>
</RequirePermission>
);
};
@@ -1,13 +1,13 @@
import { organizationsPermissions } from "api/queries/organizations";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useDashboard } from "modules/dashboard/useDashboard";
import {
canEditOrganization,
useManagementSettings,
} from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { useQuery } from "react-query";
import { useLocation, useParams } from "react-router-dom";
import {
canEditOrganization,
useOrganizationSettings,
} from "./ManagementSettingsLayout";
import { type OrganizationWithPermissions, SidebarView } from "./SidebarView";
/**
@@ -20,8 +20,7 @@ import { type OrganizationWithPermissions, SidebarView } from "./SidebarView";
export const Sidebar: FC = () => {
const location = useLocation();
const { permissions } = useAuthenticated();
const { experiments } = useDashboard();
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const { organization: organizationName } = useParams() as {
organization?: string;
};
@@ -56,7 +55,6 @@ export const Sidebar: FC = () => {
activeOrganizationName={organizationName}
organizations={editableOrgs}
permissions={permissions}
experiments={experiments}
/>
);
};
@@ -8,9 +8,10 @@ import { withDashboardProvider } from "testHelpers/storybook";
import { SidebarView } from "./SidebarView";
const meta: Meta<typeof SidebarView> = {
title: "components/MultiOrgSidebarView",
title: "modules/management/SidebarView",
component: SidebarView,
decorators: [withDashboardProvider],
parameters: { showOrganizations: true },
args: {
activeSettings: true,
activeOrganizationName: undefined,
@@ -35,7 +36,6 @@ const meta: Meta<typeof SidebarView> = {
},
],
permissions: MockPermissions,
experiments: ["notifications"],
},
};
@@ -223,3 +223,9 @@ export const SelectedMultiOrgAdminAndUserAdmin: Story = {
],
},
};
export const OrgsDisabled: Story = {
parameters: {
showOrganizations: false,
},
};
@@ -31,8 +31,6 @@ interface SidebarProps {
organizations: OrganizationWithPermissions[] | undefined;
/** Site-wide permissions. */
permissions: AuthorizationResponse;
/** Active experiments */
experiments: Experiments;
}
/**
@@ -43,25 +41,29 @@ export const SidebarView: FC<SidebarProps> = ({
activeOrganizationName,
organizations,
permissions,
experiments,
}) => {
const { showOrganizations } = useDashboard();
// TODO: Do something nice to scroll to the active org.
return (
<BaseSidebar>
<header>
<h2 css={styles.sidebarHeader}>Deployment</h2>
</header>
{showOrganizations && (
<header>
<h2 css={styles.sidebarHeader}>Deployment</h2>
</header>
)}
<DeploymentSettingsNavigation
active={!activeOrganizationName && activeSettings}
experiments={experiments}
permissions={permissions}
/>
<OrganizationsSettingsNavigation
activeOrganizationName={activeOrganizationName}
organizations={organizations}
permissions={permissions}
/>
{showOrganizations && (
<OrganizationsSettingsNavigation
activeOrganizationName={activeOrganizationName}
organizations={organizations}
permissions={permissions}
/>
)}
</BaseSidebar>
);
};
@@ -71,8 +73,6 @@ interface DeploymentSettingsNavigationProps {
active: boolean;
/** Site-wide permissions. */
permissions: AuthorizationResponse;
/** Active experiments */
experiments: Experiments;
}
/**
@@ -85,7 +85,6 @@ interface DeploymentSettingsNavigationProps {
const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
active,
permissions,
experiments,
}) => {
return (
<div css={{ paddingBottom: 12 }}>
@@ -144,16 +143,14 @@ const DeploymentSettingsNavigation: FC<DeploymentSettingsNavigationProps> = ({
</SidebarNavSubItem>
)}
{permissions.viewAllUsers && (
<SidebarNavSubItem href={linkToUsers.slice(1)}>
Users
</SidebarNavSubItem>
<SidebarNavSubItem href="users">Users</SidebarNavSubItem>
)}
<Stack direction="row" alignItems="center" css={{ gap: 0 }}>
<SidebarNavSubItem href="notifications">
Notifications
</SidebarNavSubItem>
<FeatureStageBadge contentType="beta" size="sm" />
</Stack>
<SidebarNavSubItem href="notifications">
<Stack direction="row" alignItems="center" spacing={1}>
<span>Notifications</span>
<FeatureStageBadge contentType="beta" size="sm" />
</Stack>
</SidebarNavSubItem>
</Stack>
)}
</div>
@@ -387,49 +384,49 @@ const styles = {
const classNames = {
link: (css, theme) => css`
color: inherit;
display: block;
font-size: 14px;
text-decoration: none;
padding: 10px 12px 10px 16px;
border-radius: 4px;
transition: background-color 0.15s ease-in-out;
position: relative;
color: inherit;
display: block;
font-size: 14px;
text-decoration: none;
padding: 10px 12px 10px 16px;
border-radius: 4px;
transition: background-color 0.15s ease-in-out;
position: relative;
&:hover {
background-color: ${theme.palette.action.hover};
}
&:hover {
background-color: ${theme.palette.action.hover};
}
border-left: 3px solid transparent;
`,
border-left: 3px solid transparent;
`,
activeLink: (css, theme) => css`
border-left-color: ${theme.palette.primary.main};
border-top-left-radius: 0;
border-bottom-left-radius: 0;
`,
border-left-color: ${theme.palette.primary.main};
border-top-left-radius: 0;
border-bottom-left-radius: 0;
`,
subLink: (css, theme) => css`
color: ${theme.palette.text.secondary};
text-decoration: none;
color: ${theme.palette.text.secondary};
text-decoration: none;
display: block;
font-size: 13px;
margin-left: 44px;
padding: 4px 12px;
border-radius: 4px;
transition: background-color 0.15s ease-in-out;
margin-bottom: 1px;
position: relative;
display: block;
font-size: 13px;
margin-left: 44px;
padding: 4px 12px;
border-radius: 4px;
transition: background-color 0.15s ease-in-out;
margin-bottom: 1px;
position: relative;
&:hover {
color: ${theme.palette.text.primary};
background-color: ${theme.palette.action.hover};
}
`,
&:hover {
color: ${theme.palette.text.primary};
background-color: ${theme.palette.action.hover};
}
`,
activeSubLink: (css, theme) => css`
color: ${theme.palette.text.primary};
font-weight: 600;
`,
color: ${theme.palette.text.primary};
font-weight: 600;
`,
} satisfies Record<string, ClassName>;
+1 -1
View File
@@ -22,7 +22,7 @@ export function withFilter(path: string, filter: string) {
export const linkToAuditing = "/audit";
export const linkToUsers = withFilter("/users", "status:active");
export const linkToUsers = withFilter("/deployment/users", "status:active");
export const linkToTemplate =
(organizationName: string, templateName: string): LinkThunk =>
@@ -1,67 +0,0 @@
import type { DeploymentConfig } from "api/api";
import { deploymentConfig } from "api/queries/deployment";
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 { RequirePermission } from "contexts/auth/RequirePermission";
import { useDashboard } from "modules/dashboard/useDashboard";
import { ManagementSettingsLayout } from "pages/ManagementSettingsPage/ManagementSettingsLayout";
import { type FC, Suspense, createContext, useContext } from "react";
import { useQuery } from "react-query";
import { Outlet } from "react-router-dom";
import { Sidebar } from "./Sidebar";
type DeploySettingsContextValue = {
deploymentValues: DeploymentConfig | undefined;
};
export const DeploySettingsContext = createContext<
DeploySettingsContextValue | undefined
>(undefined);
export const useDeploySettings = (): DeploySettingsContextValue => {
const context = useContext(DeploySettingsContext);
if (!context) {
throw new Error(
"useDeploySettings should be used inside of DeploySettingsContext or DeploySettingsLayout",
);
}
return context;
};
export const DeploySettingsLayout: FC = () => {
const { showOrganizations } = useDashboard();
return showOrganizations ? (
<ManagementSettingsLayout />
) : (
<DeploySettingsLayoutInner />
);
};
const DeploySettingsLayoutInner: FC = () => {
const deploymentConfigQuery = useQuery(deploymentConfig());
const { permissions } = useAuthenticated();
return (
<RequirePermission isFeatureVisible={permissions.viewDeploymentValues}>
<Margins>
<Stack css={{ padding: "48px 0" }} direction="row" spacing={6}>
<Sidebar />
<main css={{ maxWidth: 800, width: "100%" }}>
<DeploySettingsContext.Provider
value={{
deploymentValues: deploymentConfigQuery.data,
}}
>
<Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
</DeploySettingsContext.Provider>
</main>
</Stack>
</Margins>
</RequirePermission>
);
};
@@ -1,60 +0,0 @@
import Brush from "@mui/icons-material/Brush";
import HubOutlinedIcon from "@mui/icons-material/HubOutlined";
import InsertChartIcon from "@mui/icons-material/InsertChart";
import LaunchOutlined from "@mui/icons-material/LaunchOutlined";
import LockRounded from "@mui/icons-material/LockOutlined";
import NotificationsIcon from "@mui/icons-material/NotificationsNoneOutlined";
import Globe from "@mui/icons-material/PublicOutlined";
import ApprovalIcon from "@mui/icons-material/VerifiedUserOutlined";
import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined";
import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge";
import { GitIcon } from "components/Icons/GitIcon";
import {
Sidebar as BaseSidebar,
SidebarNavItem,
} from "components/Sidebar/Sidebar";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
export const Sidebar: FC = () => {
const { experiments } = useDashboard();
return (
<BaseSidebar>
<SidebarNavItem href="general" icon={LaunchOutlined}>
General
</SidebarNavItem>
<SidebarNavItem href="licenses" icon={ApprovalIcon}>
Licenses
</SidebarNavItem>
<SidebarNavItem href="appearance" icon={Brush}>
Appearance
</SidebarNavItem>
<SidebarNavItem href="userauth" icon={VpnKeyOutlined}>
User Authentication
</SidebarNavItem>
<SidebarNavItem href="external-auth" icon={GitIcon}>
External Authentication
</SidebarNavItem>
{/* Not exposing this yet since token exchange is not finished yet.
<SidebarNavItem href="oauth2-provider/apps" icon={Token}>
OAuth2 Applications
</SidebarNavItem>*/}
<SidebarNavItem href="network" icon={Globe}>
Network
</SidebarNavItem>
<SidebarNavItem href="workspace-proxies" icon={HubOutlinedIcon}>
Workspace Proxies
</SidebarNavItem>
<SidebarNavItem href="security" icon={LockRounded}>
Security
</SidebarNavItem>
<SidebarNavItem href="observability" icon={InsertChartIcon}>
Observability
</SidebarNavItem>
<SidebarNavItem href="notifications" icon={NotificationsIcon}>
Notifications <FeatureStageBadge contentType="beta" size="sm" />
</SidebarNavItem>
</BaseSidebar>
);
};
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { AnnouncementBannerDialog } from "./AnnouncementBannerDialog";
const meta: Meta<typeof AnnouncementBannerDialog> = {
title: "pages/DeploySettingsPage/AnnouncementBannerDialog",
title: "pages/DeploymentSettingsPage/AnnouncementBannerDialog",
component: AnnouncementBannerDialog,
args: {
banner: {
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { AppearanceSettingsPageView } from "./AppearanceSettingsPageView";
const meta: Meta<typeof AppearanceSettingsPageView> = {
title: "pages/DeploySettingsPage/AppearanceSettingsPageView",
title: "pages/DeploymentSettingsPage/AppearanceSettingsPageView",
component: AppearanceSettingsPageView,
args: {
appearance: {
@@ -1,12 +1,12 @@
import { Loader } from "components/Loader/Loader";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import { ExternalAuthSettingsPageView } from "./ExternalAuthSettingsPageView";
const ExternalAuthSettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
return (
<>
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
import { ExternalAuthSettingsPageView } from "./ExternalAuthSettingsPageView";
const meta: Meta<typeof ExternalAuthSettingsPageView> = {
title: "pages/DeploySettingsPage/ExternalAuthSettingsPageView",
title: "pages/DeploymentSettingsPage/ExternalAuthSettingsPageView",
component: ExternalAuthSettingsPageView,
args: {
config: {
@@ -3,15 +3,15 @@ import { entitlements } from "api/queries/entitlements";
import { availableExperiments, experiments } from "api/queries/experiments";
import { Loader } from "components/Loader/Loader";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useQuery } from "react-query";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import { GeneralSettingsPageView } from "./GeneralSettingsPageView";
const GeneralSettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
const deploymentDAUsQuery = useQuery(deploymentDAUs());
const safeExperimentsQuery = useQuery(availableExperiments());
@@ -7,7 +7,7 @@ import {
import { GeneralSettingsPageView } from "./GeneralSettingsPageView";
const meta: Meta<typeof GeneralSettingsPageView> = {
title: "pages/DeploySettingsPage/GeneralSettingsPageView",
title: "pages/DeploymentSettingsPage/GeneralSettingsPageView",
component: GeneralSettingsPageView,
args: {
deploymentOptions: [
@@ -1,7 +1,7 @@
import { AddNewLicensePageView } from "./AddNewLicensePageView";
export default {
title: "pages/DeploySettingsPage/AddNewLicensePageView",
title: "pages/DeploymentSettingsPage/AddNewLicensePageView",
component: AddNewLicensePageView,
};
@@ -3,7 +3,7 @@ import { MockLicenseResponse } from "testHelpers/entities";
import LicensesSettingsPageView from "./LicensesSettingsPageView";
export default {
title: "pages/DeploySettingsPage/LicensesSettingsPageView",
title: "pages/DeploymentSettingsPage/LicensesSettingsPageView",
parameters: { chromatic },
component: LicensesSettingsPageView,
};
@@ -1,12 +1,12 @@
import { Loader } from "components/Loader/Loader";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import { NetworkSettingsPageView } from "./NetworkSettingsPageView";
const NetworkSettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
return (
<>
@@ -8,7 +8,7 @@ const group: SerpentGroup = {
};
const meta: Meta<typeof NetworkSettingsPageView> = {
title: "pages/DeploySettingsPage/NetworkSettingsPageView",
title: "pages/DeploymentSettingsPage/NetworkSettingsPageView",
component: NetworkSettingsPageView,
args: {
options: [
@@ -8,7 +8,7 @@ import { NotificationEvents } from "./NotificationEvents";
import { baseMeta } from "./storybookUtils";
const meta: Meta<typeof NotificationEvents> = {
title: "pages/DeploymentSettings/NotificationsPage/NotificationEvents",
title: "pages/DeploymentSettingsPage/NotificationsPage/NotificationEvents",
component: NotificationEvents,
args: {
defaultMethod: "smtp",
@@ -12,7 +12,7 @@ import { NotificationsPage } from "./NotificationsPage";
import { baseMeta } from "./storybookUtils";
const meta: Meta<typeof NotificationsPage> = {
title: "pages/DeploymentSettings/NotificationsPage",
title: "pages/DeploymentSettingsPage/NotificationsPage",
component: NotificationsPage,
...baseMeta,
};
@@ -6,6 +6,7 @@ import {
} from "api/queries/notifications";
import { Loader } from "components/Loader/Loader";
import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import { castNotificationMethod } from "modules/notifications/utils";
import { Section } from "pages/UserSettingsPage/Section";
import type { FC } from "react";
@@ -14,13 +15,12 @@ import { useQueries } from "react-query";
import { useSearchParams } from "react-router-dom";
import { deploymentGroupHasParent } from "utils/deployOptions";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import OptionsTable from "../OptionsTable";
import { NotificationEvents } from "./NotificationEvents";
export const NotificationsPage: FC = () => {
const [searchParams] = useSearchParams();
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
const [templatesByGroup, dispatchMethods] = useQueries({
queries: [
{
@@ -12,8 +12,8 @@ import {
import {
withAuthProvider,
withDashboardProvider,
withDeploySettings,
withGlobalSnackbar,
withManagementSettingsProvider,
} from "testHelpers/storybook";
import type { NotificationsPage } from "./NotificationsPage";
@@ -213,6 +213,6 @@ export const baseMeta = {
withGlobalSnackbar,
withAuthProvider,
withDashboardProvider,
withDeploySettings,
withManagementSettingsProvider,
],
} satisfies Meta<typeof NotificationsPage>;
@@ -3,7 +3,7 @@ import { mockApiError } from "testHelpers/entities";
import { CreateOAuth2AppPageView } from "./CreateOAuth2AppPageView";
const meta: Meta = {
title: "pages/DeploySettingsPage/CreateOAuth2AppPageView",
title: "pages/DeploymentSettingsPage/CreateOAuth2AppPageView",
component: CreateOAuth2AppPageView,
};
export default meta;
@@ -38,8 +38,4 @@ export const WithError: Story = {
},
};
export const Default: Story = {
args: {
// Nothing.
},
};
export const Default: Story = {};
@@ -7,7 +7,7 @@ import {
import { EditOAuth2AppPageView } from "./EditOAuth2AppPageView";
const meta: Meta = {
title: "pages/DeploySettingsPage/EditOAuth2AppPageView",
title: "pages/DeploymentSettingsPage/EditOAuth2AppPageView",
component: EditOAuth2AppPageView,
};
export default meta;
@@ -3,7 +3,7 @@ import { MockOAuth2ProviderApps } from "testHelpers/entities";
import OAuth2AppsSettingsPageView from "./OAuth2AppsSettingsPageView";
const meta: Meta = {
title: "pages/DeploySettingsPage/OAuth2AppsSettingsPageView",
title: "pages/DeploymentSettingsPage/OAuth2AppsSettingsPageView",
component: OAuth2AppsSettingsPageView,
};
export default meta;
@@ -1,14 +1,14 @@
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView";
const ObservabilitySettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
const { entitlements } = useDashboard();
const { multiple_organizations: hasPremiumLicense } = useFeatureVisibility();
@@ -8,7 +8,7 @@ const group: SerpentGroup = {
};
const meta: Meta<typeof ObservabilitySettingsPageView> = {
title: "pages/DeploySettingsPage/ObservabilitySettingsPageView",
title: "pages/DeploymentSettingsPage/ObservabilitySettingsPageView",
component: ObservabilitySettingsPageView,
args: {
options: [
@@ -1,13 +1,13 @@
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import { SecuritySettingsPageView } from "./SecuritySettingsPageView";
const SecuritySettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
const { entitlements } = useDashboard();
return (
@@ -8,7 +8,7 @@ const group: SerpentGroup = {
};
const meta: Meta<typeof SecuritySettingsPageView> = {
title: "pages/DeploySettingsPage/SecuritySettingsPageView",
title: "pages/DeploymentSettingsPage/SecuritySettingsPageView",
component: SecuritySettingsPageView,
args: {
options: [
@@ -1,12 +1,12 @@
import { Loader } from "components/Loader/Loader";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useDeploySettings } from "../DeploySettingsLayout";
import { UserAuthSettingsPageView } from "./UserAuthSettingsPageView";
const UserAuthSettingsPage: FC = () => {
const { deploymentValues } = useDeploySettings();
const { deploymentValues } = useManagementSettings();
return (
<>
@@ -13,7 +13,7 @@ const ghGroup: SerpentGroup = {
};
const meta: Meta<typeof UserAuthSettingsPageView> = {
title: "pages/DeploySettingsPage/UserAuthSettingsPageView",
title: "pages/DeploymentSettingsPage/UserAuthSettingsPageView",
component: UserAuthSettingsPageView,
args: {
options: [
@@ -8,12 +8,12 @@ import {
import type { CustomRoleRequest } from "api/typesGenerated";
import { displayError } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useNavigate, useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { useOrganizationSettings } from "../ManagementSettingsLayout";
import CreateEditRolePageView from "./CreateEditRolePageView";
export const CreateEditRolePage: FC = () => {
@@ -23,7 +23,7 @@ export const CreateEditRolePage: FC = () => {
organization: string;
roleName: string;
};
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const organization = organizations?.find((o) => o.name === organizationName);
const permissionsQuery = useQuery(organizationPermissions(organization?.id));
const createOrganizationRoleMutation = useMutation(
@@ -8,12 +8,12 @@ import { Loader } from "components/Loader/Loader";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import { type FC, useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { useOrganizationSettings } from "../ManagementSettingsLayout";
import CustomRolesPageView from "./CustomRolesPageView";
export const CustomRolesPage: FC = () => {
@@ -22,7 +22,7 @@ export const CustomRolesPage: FC = () => {
const { organization: organizationName } = useParams() as {
organization: string;
};
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const organization = organizations?.find((o) => o.name === organizationName);
const permissionsQuery = useQuery(organizationPermissions(organization?.id));
const deleteRoleMutation = useMutation(
@@ -10,12 +10,12 @@ import { Loader } from "components/Loader/Loader";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import { type FC, useEffect } from "react";
import { Helmet } from "react-helmet-async";
import { useQuery } from "react-query";
import { Navigate, Link as RouterLink, useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { useOrganizationSettings } from "../ManagementSettingsLayout";
import GroupsPageView from "./GroupsPageView";
export const GroupsPage: FC = () => {
@@ -24,7 +24,7 @@ export const GroupsPage: FC = () => {
organization: string;
};
const groupsQuery = useQuery(groupsByOrganization(organizationName));
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const organization = organizations?.find((o) => o.name === organizationName);
const permissionsQuery = useQuery(organizationPermissions(organization?.id));
@@ -11,13 +11,13 @@ import { Paywall } from "components/Paywall/Paywall";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useQueries } from "react-query";
import { useParams } from "react-router-dom";
import { docs } from "utils/docs";
import { pageTitle } from "utils/page";
import { useOrganizationSettings } from "../ManagementSettingsLayout";
import { IdpSyncHelpTooltip } from "./IdpSyncHelpTooltip";
import IdpSyncPageView from "./IdpSyncPageView";
@@ -27,7 +27,7 @@ export const IdpSyncPage: FC = () => {
};
// IdP sync does not have its own entitlement and is based on templace_rbac
const { template_rbac: isIdpSyncEnabled } = useFeatureVisibility();
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const organization = organizations?.find((o) => o.name === organizationName);
const [groupIdpSyncSettingsQuery, roleIdpSyncSettingsQuery, groupsQuery] =
@@ -15,10 +15,10 @@ import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
import { Stack } from "components/Stack/Stack";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import { type FC, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useParams } from "react-router-dom";
import { useOrganizationSettings } from "./ManagementSettingsLayout";
import { OrganizationMembersPageView } from "./OrganizationMembersPageView";
const OrganizationMembersPage: FC = () => {
@@ -50,7 +50,7 @@ const OrganizationMembersPage: FC = () => {
updateOrganizationMemberRoles(queryClient, organizationName),
);
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const organization = organizations?.find((o) => o.name === organizationName);
const permissionsQuery = useQuery(organizationPermissions(organization?.id));
@@ -1,27 +1,20 @@
import { buildInfo } from "api/queries/buildInfo";
import {
organizationsPermissions,
provisionerDaemonGroups,
} from "api/queries/organizations";
import { provisionerDaemonGroups } from "api/queries/organizations";
import type { Organization } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { EmptyState } from "components/EmptyState/EmptyState";
import { Loader } from "components/Loader/Loader";
import { Paywall } from "components/Paywall/Paywall";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { useDashboard } from "modules/dashboard/useDashboard";
import { useManagementSettings } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { useQuery } from "react-query";
import { useParams } from "react-router-dom";
import { docs } from "utils/docs";
import { useOrganizationSettings } from "./ManagementSettingsLayout";
import { OrganizationProvisionersPageView } from "./OrganizationProvisionersPageView";
const OrganizationProvisionersPage: FC = () => {
const { organization: organizationName } = useParams() as {
organization: string;
};
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const { entitlements } = useDashboard();
const { metadata } = useEmbeddedMetadata();
@@ -1,14 +1,23 @@
import type { Meta, StoryObj } from "@storybook/react";
import { reactRouterParameters } from "storybook-addon-remix-react-router";
import { MockDefaultOrganization, MockUser } from "testHelpers/entities";
import { withAuthProvider, withDashboardProvider } from "testHelpers/storybook";
import {
withAuthProvider,
withDashboardProvider,
withManagementSettingsProvider,
} from "testHelpers/storybook";
import OrganizationSettingsPage from "./OrganizationSettingsPage";
const meta: Meta<typeof OrganizationSettingsPage> = {
title: "pages/OrganizationSettingsPage",
component: OrganizationSettingsPage,
decorators: [withAuthProvider, withDashboardProvider],
decorators: [
withAuthProvider,
withDashboardProvider,
withManagementSettingsProvider,
],
parameters: {
showOrganizations: true,
user: MockUser,
features: ["multiple_organizations"],
permissions: { viewDeploymentValues: true },
@@ -9,13 +9,13 @@ import { EmptyState } from "components/EmptyState/EmptyState";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { Loader } from "components/Loader/Loader";
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
import {
canEditOrganization,
useManagementSettings,
} from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { Navigate, useNavigate, useParams } from "react-router-dom";
import {
canEditOrganization,
useOrganizationSettings,
} from "./ManagementSettingsLayout";
import { OrganizationSettingsPageView } from "./OrganizationSettingsPageView";
import { OrganizationSummaryPageView } from "./OrganizationSummaryPageView";
@@ -23,7 +23,7 @@ const OrganizationSettingsPage: FC = () => {
const { organization: organizationName } = useParams() as {
organization?: string;
};
const { organizations } = useOrganizationSettings();
const { organizations } = useManagementSettings();
const feats = useFeatureVisibility();
const navigate = useNavigate();
@@ -4,6 +4,7 @@ import {
MockDefaultOrganization,
MockOrganization,
} from "testHelpers/entities";
import { withManagementSettingsProvider } from "testHelpers/storybook";
import { OrganizationSettingsPageView } from "./OrganizationSettingsPageView";
const meta: Meta<typeof OrganizationSettingsPageView> = {
+2 -3
View File
@@ -40,7 +40,7 @@ const UsersPage: FC = () => {
const navigate = useNavigate();
const location = useLocation();
const searchParamsResult = useSearchParams();
const { entitlements, showOrganizations } = useDashboard();
const { entitlements } = useDashboard();
const [searchParams] = searchParamsResult;
const groupsByUserIdQuery = useQuery(groupsByUserId());
@@ -102,7 +102,7 @@ const UsersPage: FC = () => {
authMethodsQuery.isLoading ||
groupsByUserIdQuery.isLoading;
if (showOrganizations && location.pathname !== "/deployment/users") {
if (location.pathname === "/users") {
return <Navigate to={`/deployment/users${location.search}`} replace />;
}
@@ -159,7 +159,6 @@ const UsersPage: FC = () => {
menus: { status: statusMenu },
}}
usersQuery={usersQuery}
canViewOrganizations={showOrganizations}
canCreateUser={canCreateUser}
/>
+15 -18
View File
@@ -63,30 +63,27 @@ export const UsersPageView: FC<UsersPageViewProps> = ({
authMethods,
groupsByUserId,
usersQuery,
canViewOrganizations,
canCreateUser,
}) => {
const navigate = useNavigate();
return (
<>
{canViewOrganizations && (
<PageHeader
css={{ paddingTop: 0 }}
actions={
canCreateUser && (
<Button
onClick={() => navigate("create")}
startIcon={<PersonAdd />}
>
Create user
</Button>
)
}
>
<PageHeaderTitle>Users</PageHeaderTitle>
</PageHeader>
)}
<PageHeader
css={{ paddingTop: 0 }}
actions={
canCreateUser && (
<Button
onClick={() => navigate("create")}
startIcon={<PersonAdd />}
>
Create user
</Button>
)
}
>
<PageHeaderTitle>Users</PageHeaderTitle>
</PageHeader>
<UsersFilter {...filterProps} />
+19 -16
View File
@@ -10,11 +10,10 @@ import {
import { Loader } from "./components/Loader/Loader";
import { RequireAuth } from "./contexts/auth/RequireAuth";
import { DashboardLayout } from "./modules/dashboard/DashboardLayout";
import { ManagementSettingsLayout } from "./modules/management/ManagementSettingsLayout";
import AuditPage from "./pages/AuditPage/AuditPage";
import { DeploySettingsLayout } from "./pages/DeploySettingsPage/DeploySettingsLayout";
import { HealthLayout } from "./pages/HealthPage/HealthLayout";
import LoginPage from "./pages/LoginPage/LoginPage";
import { ManagementSettingsLayout } from "./pages/ManagementSettingsPage/ManagementSettingsLayout";
import { SetupPage } from "./pages/SetupPage/SetupPage";
import { TemplateLayout } from "./pages/TemplatePage/TemplateLayout";
import { TemplateSettingsLayout } from "./pages/TemplateSettingsPage/TemplateSettingsLayout";
@@ -96,61 +95,61 @@ const SettingsGroupPage = lazy(
const GeneralSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/GeneralSettingsPage/GeneralSettingsPage"
"./pages/DeploymentSettingsPage/GeneralSettingsPage/GeneralSettingsPage"
),
);
const SecuritySettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/SecuritySettingsPage/SecuritySettingsPage"
"./pages/DeploymentSettingsPage/SecuritySettingsPage/SecuritySettingsPage"
),
);
const AppearanceSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage"
"./pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPage"
),
);
const UserAuthSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/UserAuthSettingsPage/UserAuthSettingsPage"
"./pages/DeploymentSettingsPage/UserAuthSettingsPage/UserAuthSettingsPage"
),
);
const ExternalAuthSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage"
"./pages/DeploymentSettingsPage/ExternalAuthSettingsPage/ExternalAuthSettingsPage"
),
);
const OAuth2AppsSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage"
"./pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage"
),
);
const EditOAuth2AppPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/OAuth2AppsSettingsPage/EditOAuth2AppPage"
"./pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/EditOAuth2AppPage"
),
);
const CreateOAuth2AppPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPage"
"./pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPage"
),
);
const NetworkSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/NetworkSettingsPage/NetworkSettingsPage"
"./pages/DeploymentSettingsPage/NetworkSettingsPage/NetworkSettingsPage"
),
);
const ObservabilitySettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage"
"./pages/DeploymentSettingsPage/ObservabilitySettingsPage/ObservabilitySettingsPage"
),
);
const ExternalAuthPage = lazy(
@@ -215,12 +214,14 @@ const TemplateSettingsPage = lazy(
const LicensesSettingsPage = lazy(
() =>
import(
"./pages/DeploySettingsPage/LicensesSettingsPage/LicensesSettingsPage"
"./pages/DeploymentSettingsPage/LicensesSettingsPage/LicensesSettingsPage"
),
);
const AddNewLicensePage = lazy(
() =>
import("./pages/DeploySettingsPage/LicensesSettingsPage/AddNewLicensePage"),
import(
"./pages/DeploymentSettingsPage/LicensesSettingsPage/AddNewLicensePage"
),
);
const CreateOrganizationPage = lazy(
() => import("./pages/ManagementSettingsPage/CreateOrganizationPage"),
@@ -282,7 +283,9 @@ const UserNotificationsPage = lazy(
);
const DeploymentNotificationsPage = lazy(
() =>
import("./pages/DeploySettingsPage/NotificationsPage/NotificationsPage"),
import(
"./pages/DeploymentSettingsPage/NotificationsPage/NotificationsPage"
),
);
const RoutesWithSuspense = () => {
@@ -413,7 +416,7 @@ export const router = createBrowserRouter(
</Route>
</Route>
<Route path="/deployment" element={<DeploySettingsLayout />}>
<Route path="/deployment" element={<ManagementSettingsLayout />}>
<Route path="general" element={<GeneralSettingsPage />} />
<Route path="licenses" element={<LicensesSettingsPage />} />
<Route path="licenses/add" element={<AddNewLicensePage />} />
+1 -1
View File
@@ -9,7 +9,7 @@ import { ThemeProvider } from "contexts/ThemeProvider";
import { RequireAuth } from "contexts/auth/RequireAuth";
import { DashboardLayout } from "modules/dashboard/DashboardLayout";
import type { DashboardProvider } from "modules/dashboard/DashboardProvider";
import { ManagementSettingsLayout } from "pages/ManagementSettingsPage/ManagementSettingsLayout";
import { ManagementSettingsLayout } from "modules/management/ManagementSettingsLayout";
import { TemplateSettingsLayout } from "pages/TemplateSettingsPage/TemplateSettingsLayout";
import { WorkspaceSettingsLayout } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
import type { ReactNode } from "react";
+8 -8
View File
@@ -7,12 +7,13 @@ import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar";
import { AuthProvider } from "contexts/auth/AuthProvider";
import { permissionsToCheck } from "contexts/auth/permissions";
import { DashboardContext } from "modules/dashboard/DashboardProvider";
import { DeploySettingsContext } from "pages/DeploySettingsPage/DeploySettingsLayout";
import { ManagementSettingsContext } from "modules/management/ManagementSettingsLayout";
import type { FC } from "react";
import { useQueryClient } from "react-query";
import {
MockAppearanceConfig,
MockDefaultOrganization,
MockDeploymentConfig,
MockEntitlements,
} from "./entities";
@@ -126,17 +127,16 @@ export const withGlobalSnackbar = (Story: FC) => (
</>
);
export const withDeploySettings = (Story: FC, { parameters }: StoryContext) => {
export const withManagementSettingsProvider = (Story: FC) => {
return (
<DeploySettingsContext.Provider
<ManagementSettingsContext.Provider
value={{
deploymentValues: {
config: parameters.deploymentValues ?? {},
options: parameters.deploymentOptions ?? [],
},
deploymentValues: MockDeploymentConfig,
organizations: [MockDefaultOrganization],
organization: MockDefaultOrganization,
}}
>
<Story />
</DeploySettingsContext.Provider>
</ManagementSettingsContext.Provider>
);
};