mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
chore: perform several small frontend permissions refactors (#16735)
This commit is contained in:
@ -167,8 +167,6 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: It would be nice to enforce this at the schema level
|
|
||||||
// but unfortunately our org_members table does not have an ID.
|
|
||||||
_, err := database.ExpectOne(api.Database.OrganizationMembers(ctx, database.OrganizationMembersParams{
|
_, err := database.ExpectOne(api.Database.OrganizationMembers(ctx, database.OrganizationMembersParams{
|
||||||
OrganizationID: group.OrganizationID,
|
OrganizationID: group.OrganizationID,
|
||||||
UserID: uuid.MustParse(id),
|
UserID: uuid.MustParse(id),
|
||||||
|
@ -20,10 +20,10 @@ export const defaultPassword = "SomeSecurePassword!";
|
|||||||
|
|
||||||
// Credentials for users
|
// Credentials for users
|
||||||
export const users = {
|
export const users = {
|
||||||
admin: {
|
owner: {
|
||||||
username: "admin",
|
username: "owner",
|
||||||
password: defaultPassword,
|
password: defaultPassword,
|
||||||
email: "admin@coder.com",
|
email: "owner@coder.com",
|
||||||
},
|
},
|
||||||
templateAdmin: {
|
templateAdmin: {
|
||||||
username: "template-admin",
|
username: "template-admin",
|
||||||
@ -41,7 +41,7 @@ export const users = {
|
|||||||
username: "auditor",
|
username: "auditor",
|
||||||
password: defaultPassword,
|
password: defaultPassword,
|
||||||
email: "auditor@coder.com",
|
email: "auditor@coder.com",
|
||||||
roles: ["Template Admin", "Auditor"],
|
roles: ["Auditor"],
|
||||||
},
|
},
|
||||||
member: {
|
member: {
|
||||||
username: "member",
|
username: "member",
|
||||||
|
@ -67,7 +67,7 @@ export type LoginOptions = {
|
|||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function login(page: Page, options: LoginOptions = users.admin) {
|
export async function login(page: Page, options: LoginOptions = users.owner) {
|
||||||
const ctx = page.context();
|
const ctx = page.context();
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: reset the current user
|
// biome-ignore lint/suspicious/noExplicitAny: reset the current user
|
||||||
(ctx as any)[Symbol.for("currentUser")] = undefined;
|
(ctx as any)[Symbol.for("currentUser")] = undefined;
|
||||||
|
@ -16,8 +16,8 @@ test("setup deployment", async ({ page }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup first user
|
// Setup first user
|
||||||
await page.getByLabel(Language.emailLabel).fill(users.admin.email);
|
await page.getByLabel(Language.emailLabel).fill(users.owner.email);
|
||||||
await page.getByLabel(Language.passwordLabel).fill(users.admin.password);
|
await page.getByLabel(Language.passwordLabel).fill(users.owner.password);
|
||||||
await page.getByTestId("create").click();
|
await page.getByTestId("create").click();
|
||||||
|
|
||||||
await expectUrl(page).toHavePathName("/workspaces");
|
await expectUrl(page).toHavePathName("/workspaces");
|
||||||
@ -25,7 +25,7 @@ test("setup deployment", async ({ page }) => {
|
|||||||
|
|
||||||
for (const user of Object.values(users)) {
|
for (const user of Object.values(users)) {
|
||||||
// Already created as first user
|
// Already created as first user
|
||||||
if (user.username === "admin") {
|
if (user.username === "owner") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,19 +13,17 @@ test.describe.configure({ mode: "parallel" });
|
|||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
beforeCoderTest(page);
|
beforeCoderTest(page);
|
||||||
await login(page, users.auditor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function resetSearch(page: Page) {
|
async function resetSearch(page: Page, username: string) {
|
||||||
const clearButton = page.getByLabel("Clear search");
|
const clearButton = page.getByLabel("Clear search");
|
||||||
if (await clearButton.isVisible()) {
|
if (await clearButton.isVisible()) {
|
||||||
await clearButton.click();
|
await clearButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by the auditor test user to prevent race conditions
|
// Filter by the auditor test user to prevent race conditions
|
||||||
const user = currentUser(page);
|
|
||||||
await expect(page.getByText("All users")).toBeVisible();
|
await expect(page.getByText("All users")).toBeVisible();
|
||||||
await page.getByPlaceholder("Search...").fill(`username:${user.username}`);
|
await page.getByPlaceholder("Search...").fill(`username:${username}`);
|
||||||
await expect(page.getByText("All users")).not.toBeVisible();
|
await expect(page.getByText("All users")).not.toBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,12 +31,14 @@ test("logins are logged", async ({ page }) => {
|
|||||||
requiresLicense();
|
requiresLicense();
|
||||||
|
|
||||||
// Go to the audit history
|
// Go to the audit history
|
||||||
|
await login(page, users.auditor);
|
||||||
await page.goto("/audit");
|
await page.goto("/audit");
|
||||||
|
const username = users.auditor.username;
|
||||||
|
|
||||||
const user = currentUser(page);
|
const user = currentUser(page);
|
||||||
const loginMessage = `${user.username} logged in`;
|
const loginMessage = `${username} logged in`;
|
||||||
// Make sure those things we did all actually show up
|
// Make sure those things we did all actually show up
|
||||||
await resetSearch(page);
|
await resetSearch(page, username);
|
||||||
await expect(page.getByText(loginMessage).first()).toBeVisible();
|
await expect(page.getByText(loginMessage).first()).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -46,29 +46,30 @@ test("creating templates and workspaces is logged", async ({ page }) => {
|
|||||||
requiresLicense();
|
requiresLicense();
|
||||||
|
|
||||||
// Do some stuff that should show up in the audit logs
|
// Do some stuff that should show up in the audit logs
|
||||||
|
await login(page, users.templateAdmin);
|
||||||
|
const username = users.templateAdmin.username;
|
||||||
const templateName = await createTemplate(page);
|
const templateName = await createTemplate(page);
|
||||||
const workspaceName = await createWorkspace(page, templateName);
|
const workspaceName = await createWorkspace(page, templateName);
|
||||||
|
|
||||||
// Go to the audit history
|
// Go to the audit history
|
||||||
|
await login(page, users.auditor);
|
||||||
await page.goto("/audit");
|
await page.goto("/audit");
|
||||||
|
|
||||||
const user = currentUser(page);
|
|
||||||
|
|
||||||
// Make sure those things we did all actually show up
|
// Make sure those things we did all actually show up
|
||||||
await resetSearch(page);
|
await resetSearch(page, username);
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText(`${user.username} created template ${templateName}`),
|
page.getByText(`${username} created template ${templateName}`),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText(`${user.username} created workspace ${workspaceName}`),
|
page.getByText(`${username} created workspace ${workspaceName}`),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText(`${user.username} started workspace ${workspaceName}`),
|
page.getByText(`${username} started workspace ${workspaceName}`),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
// Make sure we can inspect the details of the log item
|
// Make sure we can inspect the details of the log item
|
||||||
const createdWorkspace = page.locator(".MuiTableRow-root", {
|
const createdWorkspace = page.locator(".MuiTableRow-root", {
|
||||||
hasText: `${user.username} created workspace ${workspaceName}`,
|
hasText: `${username} created workspace ${workspaceName}`,
|
||||||
});
|
});
|
||||||
await createdWorkspace.getByLabel("open-dropdown").click();
|
await createdWorkspace.getByLabel("open-dropdown").click();
|
||||||
await expect(
|
await expect(
|
||||||
@ -83,18 +84,19 @@ test("inspecting and filtering audit logs", async ({ page }) => {
|
|||||||
requiresLicense();
|
requiresLicense();
|
||||||
|
|
||||||
// Do some stuff that should show up in the audit logs
|
// Do some stuff that should show up in the audit logs
|
||||||
|
await login(page, users.templateAdmin);
|
||||||
|
const username = users.templateAdmin.username;
|
||||||
const templateName = await createTemplate(page);
|
const templateName = await createTemplate(page);
|
||||||
const workspaceName = await createWorkspace(page, templateName);
|
const workspaceName = await createWorkspace(page, templateName);
|
||||||
|
|
||||||
// Go to the audit history
|
// Go to the audit history
|
||||||
|
await login(page, users.auditor);
|
||||||
await page.goto("/audit");
|
await page.goto("/audit");
|
||||||
|
const loginMessage = `${username} logged in`;
|
||||||
const user = currentUser(page);
|
const startedWorkspaceMessage = `${username} started workspace ${workspaceName}`;
|
||||||
const loginMessage = `${user.username} logged in`;
|
|
||||||
const startedWorkspaceMessage = `${user.username} started workspace ${workspaceName}`;
|
|
||||||
|
|
||||||
// Filter by resource type
|
// Filter by resource type
|
||||||
await resetSearch(page);
|
await resetSearch(page, username);
|
||||||
await page.getByText("All resource types").click();
|
await page.getByText("All resource types").click();
|
||||||
const workspaceBuildsOption = page.getByText("Workspace Build");
|
const workspaceBuildsOption = page.getByText("Workspace Build");
|
||||||
await workspaceBuildsOption.scrollIntoViewIfNeeded({ timeout: 5000 });
|
await workspaceBuildsOption.scrollIntoViewIfNeeded({ timeout: 5000 });
|
||||||
@ -107,7 +109,7 @@ test("inspecting and filtering audit logs", async ({ page }) => {
|
|||||||
await expect(page.getByText("All resource types")).toBeVisible();
|
await expect(page.getByText("All resource types")).toBeVisible();
|
||||||
|
|
||||||
// Filter by action type
|
// Filter by action type
|
||||||
await resetSearch(page);
|
await resetSearch(page, username);
|
||||||
await page.getByText("All actions").click();
|
await page.getByText("All actions").click();
|
||||||
await page.getByText("Login", { exact: true }).click();
|
await page.getByText("Login", { exact: true }).click();
|
||||||
// Logins should be visible
|
// Logins should be visible
|
||||||
|
@ -16,7 +16,7 @@ test("experiments", async ({ page }) => {
|
|||||||
const availableExperiments = await API.getAvailableExperiments();
|
const availableExperiments = await API.getAvailableExperiments();
|
||||||
|
|
||||||
// Verify if the site lists the same experiments
|
// Verify if the site lists the same experiments
|
||||||
await page.goto("/deployment/general", { waitUntil: "networkidle" });
|
await page.goto("/deployment/overview", { waitUntil: "domcontentloaded" });
|
||||||
|
|
||||||
const experimentsLocator = page.locator(
|
const experimentsLocator = page.locator(
|
||||||
"div.options-table tr.option-experiments ul.option-array",
|
"div.options-table tr.option-experiments ul.option-array",
|
||||||
|
@ -82,8 +82,8 @@ test.describe("roles admin settings access", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("admin can see admin settings", async ({ page }) => {
|
test("owner can see admin settings", async ({ page }) => {
|
||||||
await login(page, users.admin);
|
await login(page, users.owner);
|
||||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||||
|
|
||||||
await hasAccessToAdminSettings(page, [
|
await hasAccessToAdminSettings(page, [
|
||||||
|
2
site/src/@types/storybook.d.ts
vendored
2
site/src/@types/storybook.d.ts
vendored
@ -6,7 +6,7 @@ import type {
|
|||||||
SerpentOption,
|
SerpentOption,
|
||||||
User,
|
User,
|
||||||
} from "api/typesGenerated";
|
} from "api/typesGenerated";
|
||||||
import type { Permissions } from "contexts/auth/permissions";
|
import type { Permissions } from "modules/permissions";
|
||||||
import type { QueryKey } from "react-query";
|
import type { QueryKey } from "react-query";
|
||||||
|
|
||||||
declare module "@storybook/react" {
|
declare module "@storybook/react" {
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
type OrganizationPermissionName,
|
type OrganizationPermissionName,
|
||||||
type OrganizationPermissions,
|
type OrganizationPermissions,
|
||||||
organizationPermissionChecks,
|
organizationPermissionChecks,
|
||||||
} from "modules/management/organizationPermissions";
|
} from "modules/permissions/organizations";
|
||||||
import type { QueryClient } from "react-query";
|
import type { QueryClient } from "react-query";
|
||||||
import { meKey } from "./users";
|
import { meKey } from "./users";
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import type { UpdateUserProfileRequest, User } from "api/typesGenerated";
|
import type { UpdateUserProfileRequest, User } from "api/typesGenerated";
|
||||||
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
||||||
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
||||||
|
import { type Permissions, permissionChecks } from "modules/permissions";
|
||||||
import {
|
import {
|
||||||
type FC,
|
type FC,
|
||||||
type PropsWithChildren,
|
type PropsWithChildren,
|
||||||
@ -18,7 +19,6 @@ import {
|
|||||||
useContext,
|
useContext,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||||
import { type Permissions, permissionChecks } from "./permissions";
|
|
||||||
|
|
||||||
export type AuthContextValue = {
|
export type AuthContextValue = {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
@ -16,8 +16,8 @@ import { useUpdateCheck } from "./useUpdateCheck";
|
|||||||
|
|
||||||
export const DashboardLayout: FC = () => {
|
export const DashboardLayout: FC = () => {
|
||||||
const { permissions } = useAuthenticated();
|
const { permissions } = useAuthenticated();
|
||||||
const updateCheck = useUpdateCheck(permissions.viewUpdateCheck);
|
const updateCheck = useUpdateCheck(permissions.viewDeploymentConfig);
|
||||||
const canViewDeployment = Boolean(permissions.viewDeploymentValues);
|
const canViewDeployment = Boolean(permissions.viewDeploymentConfig);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -11,8 +11,8 @@ import type {
|
|||||||
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
||||||
import { canViewAnyOrganization } from "contexts/auth/permissions";
|
|
||||||
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
||||||
|
import { canViewAnyOrganization } from "modules/permissions";
|
||||||
import { type FC, type PropsWithChildren, createContext } from "react";
|
import { type FC, type PropsWithChildren, createContext } from "react";
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { selectFeatureVisibility } from "./entitlements";
|
import { selectFeatureVisibility } from "./entitlements";
|
||||||
|
@ -10,10 +10,10 @@ export const DeploymentBanner: FC = () => {
|
|||||||
const deploymentStatsQuery = useQuery(deploymentStats());
|
const deploymentStatsQuery = useQuery(deploymentStats());
|
||||||
const healthQuery = useQuery({
|
const healthQuery = useQuery({
|
||||||
...health(),
|
...health(),
|
||||||
enabled: permissions.viewDeploymentValues,
|
enabled: permissions.viewDeploymentConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!permissions.viewDeploymentValues || !deploymentStatsQuery.data) {
|
if (!permissions.viewDeploymentConfig || !deploymentStatsQuery.data) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { buildInfo } from "api/queries/buildInfo";
|
import { buildInfo } from "api/queries/buildInfo";
|
||||||
import { useProxy } from "contexts/ProxyContext";
|
import { useProxy } from "contexts/ProxyContext";
|
||||||
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
||||||
import { canViewDeploymentSettings } from "contexts/auth/permissions";
|
|
||||||
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
||||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||||
|
import { canViewDeploymentSettings } from "modules/permissions";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { useFeatureVisibility } from "../useFeatureVisibility";
|
import { useFeatureVisibility } from "../useFeatureVisibility";
|
||||||
|
@ -3,7 +3,7 @@ import { fn, userEvent, within } from "@storybook/test";
|
|||||||
import { getAuthorizationKey } from "api/queries/authCheck";
|
import { getAuthorizationKey } from "api/queries/authCheck";
|
||||||
import { getPreferredProxy } from "contexts/ProxyContext";
|
import { getPreferredProxy } from "contexts/ProxyContext";
|
||||||
import { AuthProvider } from "contexts/auth/AuthProvider";
|
import { AuthProvider } from "contexts/auth/AuthProvider";
|
||||||
import { permissionChecks } from "contexts/auth/permissions";
|
import { permissionChecks } from "modules/permissions";
|
||||||
import {
|
import {
|
||||||
MockAuthMethodsAll,
|
MockAuthMethodsAll,
|
||||||
MockPermissions,
|
MockPermissions,
|
||||||
|
@ -6,26 +6,26 @@ import { type FC, createContext, useContext } from "react";
|
|||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
|
|
||||||
export const DeploymentSettingsContext = createContext<
|
export const DeploymentConfigContext = createContext<
|
||||||
DeploymentSettingsValue | undefined
|
DeploymentConfigValue | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
type DeploymentSettingsValue = Readonly<{
|
type DeploymentConfigValue = Readonly<{
|
||||||
deploymentConfig: DeploymentConfig;
|
deploymentConfig: DeploymentConfig;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const useDeploymentSettings = (): DeploymentSettingsValue => {
|
export const useDeploymentConfig = (): DeploymentConfigValue => {
|
||||||
const context = useContext(DeploymentSettingsContext);
|
const context = useContext(DeploymentConfigContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${useDeploymentSettings.name} should be used inside of ${DeploymentSettingsProvider.name}`,
|
`${useDeploymentConfig.name} should be used inside of ${DeploymentConfigProvider.name}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeploymentSettingsProvider: FC = () => {
|
const DeploymentConfigProvider: FC = () => {
|
||||||
const deploymentConfigQuery = useQuery(deploymentConfig());
|
const deploymentConfigQuery = useQuery(deploymentConfig());
|
||||||
|
|
||||||
if (deploymentConfigQuery.error) {
|
if (deploymentConfigQuery.error) {
|
||||||
@ -37,12 +37,12 @@ const DeploymentSettingsProvider: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DeploymentSettingsContext.Provider
|
<DeploymentConfigContext.Provider
|
||||||
value={{ deploymentConfig: deploymentConfigQuery.data }}
|
value={{ deploymentConfig: deploymentConfigQuery.data }}
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</DeploymentSettingsContext.Provider>
|
</DeploymentConfigContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeploymentSettingsProvider;
|
export default DeploymentConfigProvider;
|
@ -7,8 +7,8 @@ import {
|
|||||||
} from "components/Breadcrumb/Breadcrumb";
|
} from "components/Breadcrumb/Breadcrumb";
|
||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
||||||
import { RequirePermission } from "contexts/auth/RequirePermission";
|
import { canViewDeploymentSettings } from "modules/permissions";
|
||||||
import { canViewDeploymentSettings } from "contexts/auth/permissions";
|
import { RequirePermission } from "modules/permissions/RequirePermission";
|
||||||
import { type FC, Suspense } from "react";
|
import { type FC, Suspense } from "react";
|
||||||
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||||
import { DeploymentSidebar } from "./DeploymentSidebar";
|
import { DeploymentSidebar } from "./DeploymentSidebar";
|
||||||
@ -21,8 +21,8 @@ const DeploymentSettingsLayout: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<Navigate
|
<Navigate
|
||||||
to={
|
to={
|
||||||
permissions.viewDeploymentValues
|
permissions.viewDeploymentConfig
|
||||||
? "/deployment/general"
|
? "/deployment/overview"
|
||||||
: "/deployment/users"
|
: "/deployment/users"
|
||||||
}
|
}
|
||||||
replace
|
replace
|
||||||
|
@ -47,8 +47,8 @@ export const NoDeploymentValues: Story = {
|
|||||||
args: {
|
args: {
|
||||||
permissions: {
|
permissions: {
|
||||||
...MockPermissions,
|
...MockPermissions,
|
||||||
viewDeploymentValues: false,
|
viewDeploymentConfig: false,
|
||||||
editDeploymentValues: false,
|
editDeploymentConfig: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -4,8 +4,8 @@ import {
|
|||||||
SettingsSidebarNavItem as SidebarNavItem,
|
SettingsSidebarNavItem as SidebarNavItem,
|
||||||
} from "components/Sidebar/Sidebar";
|
} from "components/Sidebar/Sidebar";
|
||||||
import { Stack } from "components/Stack/Stack";
|
import { Stack } from "components/Stack/Stack";
|
||||||
import type { Permissions } from "contexts/auth/permissions";
|
|
||||||
import { ArrowUpRight } from "lucide-react";
|
import { ArrowUpRight } from "lucide-react";
|
||||||
|
import type { Permissions } from "modules/permissions";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
|
|
||||||
interface DeploymentSidebarViewProps {
|
interface DeploymentSidebarViewProps {
|
||||||
@ -18,9 +18,6 @@ interface DeploymentSidebarViewProps {
|
|||||||
/**
|
/**
|
||||||
* Displays navigation for deployment settings. If active, highlight the main
|
* Displays navigation for deployment settings. If active, highlight the main
|
||||||
* menu heading.
|
* menu heading.
|
||||||
*
|
|
||||||
* Menu items are shown based on the permissions. If organizations can be
|
|
||||||
* viewed, groups are skipped since they will show under each org instead.
|
|
||||||
*/
|
*/
|
||||||
export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
||||||
permissions,
|
permissions,
|
||||||
@ -30,32 +27,32 @@ export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
|||||||
return (
|
return (
|
||||||
<BaseSidebar>
|
<BaseSidebar>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{permissions.viewDeploymentValues && (
|
{permissions.viewDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/general">General</SidebarNavItem>
|
<SidebarNavItem href="/deployment/overview">Overview</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.viewAllLicenses && (
|
{permissions.viewAllLicenses && (
|
||||||
<SidebarNavItem href="/deployment/licenses">Licenses</SidebarNavItem>
|
<SidebarNavItem href="/deployment/licenses">Licenses</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.editDeploymentValues && (
|
{permissions.editDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/appearance">
|
<SidebarNavItem href="/deployment/appearance">
|
||||||
Appearance
|
Appearance
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.viewDeploymentValues && (
|
{permissions.viewDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/userauth">
|
<SidebarNavItem href="/deployment/userauth">
|
||||||
User Authentication
|
User Authentication
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.viewDeploymentValues && (
|
{permissions.viewDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/external-auth">
|
<SidebarNavItem href="/deployment/external-auth">
|
||||||
External Authentication
|
External Authentication
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{/* Not exposing this yet since token exchange is not finished yet.
|
{/* Not exposing this yet since token exchange is not finished yet.
|
||||||
<SidebarNavItem href="oauth2-provider/ap">
|
<SidebarNavItem href="oauth2-provider/apps">
|
||||||
OAuth2 Applications
|
OAuth2 Applications
|
||||||
</SidebarNavItem>*/}
|
</SidebarNavItem>*/}
|
||||||
{permissions.viewDeploymentValues && (
|
{permissions.viewDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/network">Network</SidebarNavItem>
|
<SidebarNavItem href="/deployment/network">Network</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.readWorkspaceProxies && (
|
{permissions.readWorkspaceProxies && (
|
||||||
@ -63,10 +60,10 @@ export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
|||||||
Workspace Proxies
|
Workspace Proxies
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.viewDeploymentValues && (
|
{permissions.viewDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/security">Security</SidebarNavItem>
|
<SidebarNavItem href="/deployment/security">Security</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.viewDeploymentValues && (
|
{permissions.viewDeploymentConfig && (
|
||||||
<SidebarNavItem href="/deployment/observability">
|
<SidebarNavItem href="/deployment/observability">
|
||||||
Observability
|
Observability
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
@ -81,6 +78,11 @@ export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
|
{permissions.viewOrganizationIDPSyncSettings && (
|
||||||
|
<SidebarNavItem href="/deployment/idp-org-sync">
|
||||||
|
IdP Organization Sync
|
||||||
|
</SidebarNavItem>
|
||||||
|
)}
|
||||||
{permissions.viewNotificationTemplate && (
|
{permissions.viewNotificationTemplate && (
|
||||||
<SidebarNavItem href="/deployment/notifications">
|
<SidebarNavItem href="/deployment/notifications">
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
@ -89,11 +91,6 @@ export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
{permissions.viewOrganizationIDPSyncSettings && (
|
|
||||||
<SidebarNavItem href="/deployment/idp-org-sync">
|
|
||||||
IdP Organization Sync
|
|
||||||
</SidebarNavItem>
|
|
||||||
)}
|
|
||||||
{!hasPremiumLicense && (
|
{!hasPremiumLicense && (
|
||||||
<SidebarNavItem href="/deployment/premium">Premium</SidebarNavItem>
|
<SidebarNavItem href="/deployment/premium">Premium</SidebarNavItem>
|
||||||
)}
|
)}
|
||||||
|
@ -11,14 +11,14 @@ import {
|
|||||||
} from "components/Breadcrumb/Breadcrumb";
|
} from "components/Breadcrumb/Breadcrumb";
|
||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||||
|
import {
|
||||||
|
type OrganizationPermissions,
|
||||||
|
canViewOrganization,
|
||||||
|
} from "modules/permissions/organizations";
|
||||||
import NotFoundPage from "pages/404Page/404Page";
|
import NotFoundPage from "pages/404Page/404Page";
|
||||||
import { type FC, Suspense, createContext, useContext } from "react";
|
import { type FC, Suspense, createContext, useContext } from "react";
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { Outlet, useParams } from "react-router-dom";
|
import { Outlet, useParams } from "react-router-dom";
|
||||||
import {
|
|
||||||
type OrganizationPermissions,
|
|
||||||
canViewOrganization,
|
|
||||||
} from "./organizationPermissions";
|
|
||||||
|
|
||||||
export const OrganizationSettingsContext = createContext<
|
export const OrganizationSettingsContext = createContext<
|
||||||
OrganizationSettingsValue | undefined
|
OrganizationSettingsValue | undefined
|
||||||
@ -46,7 +46,7 @@ export const useOrganizationSettings = (): OrganizationSettingsValue => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const OrganizationSettingsLayout: FC = () => {
|
const OrganizationSettingsLayout: FC = () => {
|
||||||
const { organizations, showOrganizations } = useDashboard();
|
const { organizations } = useDashboard();
|
||||||
const { organization: orgName } = useParams() as {
|
const { organization: orgName } = useParams() as {
|
||||||
organization?: string;
|
organization?: string;
|
||||||
};
|
};
|
||||||
|
@ -16,11 +16,11 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "components/Popover/Popover";
|
} from "components/Popover/Popover";
|
||||||
import { SettingsSidebarNavItem } from "components/Sidebar/Sidebar";
|
import { SettingsSidebarNavItem } from "components/Sidebar/Sidebar";
|
||||||
import type { Permissions } from "contexts/auth/permissions";
|
|
||||||
import { Check, ChevronDown, Plus } from "lucide-react";
|
import { Check, ChevronDown, Plus } from "lucide-react";
|
||||||
|
import type { Permissions } from "modules/permissions";
|
||||||
|
import type { OrganizationPermissions } from "modules/permissions/organizations";
|
||||||
import { type FC, useState } from "react";
|
import { type FC, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import type { OrganizationPermissions } from "./organizationPermissions";
|
|
||||||
|
|
||||||
interface OrganizationsSettingsNavigationProps {
|
interface OrganizationsSettingsNavigationProps {
|
||||||
/** The organization selected from the dropdown */
|
/** The organization selected from the dropdown */
|
||||||
|
@ -30,7 +30,7 @@ export const permissionChecks = {
|
|||||||
resource_type: "template",
|
resource_type: "template",
|
||||||
any_org: true,
|
any_org: true,
|
||||||
},
|
},
|
||||||
action: "update",
|
action: "create",
|
||||||
},
|
},
|
||||||
updateTemplates: {
|
updateTemplates: {
|
||||||
object: {
|
object: {
|
||||||
@ -44,30 +44,18 @@ export const permissionChecks = {
|
|||||||
},
|
},
|
||||||
action: "delete",
|
action: "delete",
|
||||||
},
|
},
|
||||||
viewDeploymentValues: {
|
viewDeploymentConfig: {
|
||||||
object: {
|
object: {
|
||||||
resource_type: "deployment_config",
|
resource_type: "deployment_config",
|
||||||
},
|
},
|
||||||
action: "read",
|
action: "read",
|
||||||
},
|
},
|
||||||
editDeploymentValues: {
|
editDeploymentConfig: {
|
||||||
object: {
|
object: {
|
||||||
resource_type: "deployment_config",
|
resource_type: "deployment_config",
|
||||||
},
|
},
|
||||||
action: "update",
|
action: "update",
|
||||||
},
|
},
|
||||||
viewUpdateCheck: {
|
|
||||||
object: {
|
|
||||||
resource_type: "deployment_config",
|
|
||||||
},
|
|
||||||
action: "read",
|
|
||||||
},
|
|
||||||
viewExternalAuthConfig: {
|
|
||||||
object: {
|
|
||||||
resource_type: "deployment_config",
|
|
||||||
},
|
|
||||||
action: "read",
|
|
||||||
},
|
|
||||||
viewDeploymentStats: {
|
viewDeploymentStats: {
|
||||||
object: {
|
object: {
|
||||||
resource_type: "deployment_stats",
|
resource_type: "deployment_stats",
|
||||||
@ -178,7 +166,7 @@ export const canViewDeploymentSettings = (
|
|||||||
): permissions is Permissions => {
|
): permissions is Permissions => {
|
||||||
return (
|
return (
|
||||||
permissions !== undefined &&
|
permissions !== undefined &&
|
||||||
(permissions.viewDeploymentValues ||
|
(permissions.viewDeploymentConfig ||
|
||||||
permissions.viewAllLicenses ||
|
permissions.viewAllLicenses ||
|
||||||
permissions.viewAllUsers ||
|
permissions.viewAllUsers ||
|
||||||
permissions.viewAnyGroup ||
|
permissions.viewAnyGroup ||
|
@ -1,11 +1,11 @@
|
|||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { ExternalAuthSettingsPageView } from "./ExternalAuthSettingsPageView";
|
import { ExternalAuthSettingsPageView } from "./ExternalAuthSettingsPageView";
|
||||||
|
|
||||||
const ExternalAuthSettingsPage: FC = () => {
|
const ExternalAuthSettingsPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -108,7 +108,7 @@ export const LicenseSeatConsumptionChart: FC<
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link asChild>
|
<Link asChild>
|
||||||
<RouterLink to="/deployment/general">
|
<RouterLink to="/deployment/overview">
|
||||||
Daily user activity
|
Daily user activity
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { NetworkSettingsPageView } from "./NetworkSettingsPageView";
|
import { NetworkSettingsPageView } from "./NetworkSettingsPageView";
|
||||||
|
|
||||||
const NetworkSettingsPage: FC = () => {
|
const NetworkSettingsPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -9,7 +9,7 @@ import { Loader } from "components/Loader/Loader";
|
|||||||
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
|
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
|
||||||
import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs";
|
import { TabLink, Tabs, TabsList } from "components/Tabs/Tabs";
|
||||||
import { useSearchParamsKey } from "hooks/useSearchParamsKey";
|
import { useSearchParamsKey } from "hooks/useSearchParamsKey";
|
||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import { castNotificationMethod } from "modules/notifications/utils";
|
import { castNotificationMethod } from "modules/notifications/utils";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
@ -22,7 +22,7 @@ import { NotificationEvents } from "./NotificationEvents";
|
|||||||
import { Troubleshooting } from "./Troubleshooting";
|
import { Troubleshooting } from "./Troubleshooting";
|
||||||
|
|
||||||
export const NotificationsPage: FC = () => {
|
export const NotificationsPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
const [templatesByGroup, dispatchMethods] = useQueries({
|
const [templatesByGroup, dispatchMethods] = useQueries({
|
||||||
queries: [
|
queries: [
|
||||||
{
|
{
|
||||||
|
@ -194,7 +194,7 @@ export const baseMeta = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
user: MockUser,
|
user: MockUser,
|
||||||
permissions: { viewDeploymentValues: true },
|
permissions: { viewDeploymentConfig: true },
|
||||||
deploymentOptions: mockNotificationsDeploymentOptions,
|
deploymentOptions: mockNotificationsDeploymentOptions,
|
||||||
deploymentValues: {
|
deploymentValues: {
|
||||||
notifications: {
|
notifications: {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||||
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView";
|
import { ObservabilitySettingsPageView } from "./ObservabilitySettingsPageView";
|
||||||
|
|
||||||
const ObservabilitySettingsPage: FC = () => {
|
const ObservabilitySettingsPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
const { entitlements } = useDashboard();
|
const { entitlements } = useDashboard();
|
||||||
const { multiple_organizations: hasPremiumLicense } = useFeatureVisibility();
|
const { multiple_organizations: hasPremiumLicense } = useFeatureVisibility();
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { deploymentDAUs } from "api/queries/deployment";
|
import { deploymentDAUs } from "api/queries/deployment";
|
||||||
import { availableExperiments, experiments } from "api/queries/experiments";
|
import { availableExperiments, experiments } from "api/queries/experiments";
|
||||||
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
|
||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { GeneralSettingsPageView } from "./GeneralSettingsPageView";
|
import { OverviewPageView } from "./OverviewPageView";
|
||||||
|
|
||||||
const GeneralSettingsPage: FC = () => {
|
const OverviewPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
const safeExperimentsQuery = useQuery(availableExperiments());
|
const safeExperimentsQuery = useQuery(availableExperiments());
|
||||||
|
|
||||||
const { metadata } = useEmbeddedMetadata();
|
const { metadata } = useEmbeddedMetadata();
|
||||||
@ -26,9 +26,9 @@ const GeneralSettingsPage: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{pageTitle("General Settings")}</title>
|
<title>{pageTitle("Overview", "Deployment")}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<GeneralSettingsPageView
|
<OverviewPageView
|
||||||
deploymentOptions={deploymentConfig.options}
|
deploymentOptions={deploymentConfig.options}
|
||||||
dailyActiveUsers={dailyActiveUsers}
|
dailyActiveUsers={dailyActiveUsers}
|
||||||
invalidExperiments={invalidExperiments}
|
invalidExperiments={invalidExperiments}
|
||||||
@ -38,4 +38,4 @@ const GeneralSettingsPage: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default GeneralSettingsPage;
|
export default OverviewPage;
|
@ -1,10 +1,10 @@
|
|||||||
import type { Meta, StoryObj } from "@storybook/react";
|
import type { Meta, StoryObj } from "@storybook/react";
|
||||||
import { MockDeploymentDAUResponse } from "testHelpers/entities";
|
import { MockDeploymentDAUResponse } from "testHelpers/entities";
|
||||||
import { GeneralSettingsPageView } from "./GeneralSettingsPageView";
|
import { OverviewPageView } from "./OverviewPageView";
|
||||||
|
|
||||||
const meta: Meta<typeof GeneralSettingsPageView> = {
|
const meta: Meta<typeof OverviewPageView> = {
|
||||||
title: "pages/DeploymentSettingsPage/GeneralSettingsPageView",
|
title: "pages/DeploymentSettingsPage/OverviewPageView",
|
||||||
component: GeneralSettingsPageView,
|
component: OverviewPageView,
|
||||||
args: {
|
args: {
|
||||||
deploymentOptions: [
|
deploymentOptions: [
|
||||||
{
|
{
|
||||||
@ -42,7 +42,7 @@ const meta: Meta<typeof GeneralSettingsPageView> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof GeneralSettingsPageView>;
|
type Story = StoryObj<typeof OverviewPageView>;
|
||||||
|
|
||||||
export const Page: Story = {};
|
export const Page: Story = {};
|
||||||
|
|
@ -14,14 +14,14 @@ import { Alert } from "../../../components/Alert/Alert";
|
|||||||
import OptionsTable from "../OptionsTable";
|
import OptionsTable from "../OptionsTable";
|
||||||
import { UserEngagementChart } from "./UserEngagementChart";
|
import { UserEngagementChart } from "./UserEngagementChart";
|
||||||
|
|
||||||
export type GeneralSettingsPageViewProps = {
|
export type OverviewPageViewProps = {
|
||||||
deploymentOptions: SerpentOption[];
|
deploymentOptions: SerpentOption[];
|
||||||
dailyActiveUsers: DAUsResponse | undefined;
|
dailyActiveUsers: DAUsResponse | undefined;
|
||||||
readonly invalidExperiments: Experiments | string[];
|
readonly invalidExperiments: Experiments | string[];
|
||||||
readonly safeExperiments: Experiments | string[];
|
readonly safeExperiments: Experiments | string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GeneralSettingsPageView: FC<GeneralSettingsPageViewProps> = ({
|
export const OverviewPageView: FC<OverviewPageViewProps> = ({
|
||||||
deploymentOptions,
|
deploymentOptions,
|
||||||
dailyActiveUsers,
|
dailyActiveUsers,
|
||||||
safeExperiments,
|
safeExperiments,
|
@ -1,12 +1,12 @@
|
|||||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { SecuritySettingsPageView } from "./SecuritySettingsPageView";
|
import { SecuritySettingsPageView } from "./SecuritySettingsPageView";
|
||||||
|
|
||||||
const SecuritySettingsPage: FC = () => {
|
const SecuritySettingsPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
const { entitlements } = useDashboard();
|
const { entitlements } = useDashboard();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useDeploymentSettings } from "modules/management/DeploymentSettingsProvider";
|
import { useDeploymentConfig } from "modules/management/DeploymentConfigProvider";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { UserAuthSettingsPageView } from "./UserAuthSettingsPageView";
|
import { UserAuthSettingsPageView } from "./UserAuthSettingsPageView";
|
||||||
|
|
||||||
const UserAuthSettingsPage: FC = () => {
|
const UserAuthSettingsPage: FC = () => {
|
||||||
const { deploymentConfig } = useDeploymentSettings();
|
const { deploymentConfig } = useDeploymentConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -104,7 +104,7 @@ const ExternalAuthPage: FC = () => {
|
|||||||
authenticated: false,
|
authenticated: false,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
viewExternalAuthConfig={permissions.viewExternalAuthConfig}
|
viewExternalAuthConfig={permissions.viewDeploymentConfig}
|
||||||
deviceExchangeError={deviceExchangeError}
|
deviceExchangeError={deviceExchangeError}
|
||||||
externalAuthDevice={externalAuthDeviceQuery.data}
|
externalAuthDevice={externalAuthDeviceQuery.data}
|
||||||
/>
|
/>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { createOrganization } from "api/queries/organizations";
|
import { createOrganization } from "api/queries/organizations";
|
||||||
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
||||||
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
import { useAuthenticated } from "contexts/auth/RequireAuth";
|
||||||
import { RequirePermission } from "contexts/auth/RequirePermission";
|
|
||||||
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
||||||
|
import { RequirePermission } from "modules/permissions/RequirePermission";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useMutation, useQueryClient } from "react-query";
|
import { useMutation, useQueryClient } from "react-query";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
@ -8,8 +8,8 @@ import type { CustomRoleRequest } from "api/typesGenerated";
|
|||||||
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||||
import { displayError } from "components/GlobalSnackbar/utils";
|
import { displayError } from "components/GlobalSnackbar/utils";
|
||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { RequirePermission } from "contexts/auth/RequirePermission";
|
|
||||||
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
|
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
|
||||||
|
import { RequirePermission } from "modules/permissions/RequirePermission";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||||
|
@ -6,9 +6,9 @@ import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
|
|||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
|
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
|
||||||
import { Stack } from "components/Stack/Stack";
|
import { Stack } from "components/Stack/Stack";
|
||||||
import { RequirePermission } from "contexts/auth/RequirePermission";
|
|
||||||
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
|
||||||
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
|
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
|
||||||
|
import { RequirePermission } from "modules/permissions/RequirePermission";
|
||||||
import { type FC, useEffect, useState } from "react";
|
import { type FC, useEffect, useState } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { EmptyState } from "components/EmptyState/EmptyState";
|
import { EmptyState } from "components/EmptyState/EmptyState";
|
||||||
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
|
import { useOrganizationSettings } from "modules/management/OrganizationSettingsLayout";
|
||||||
import { canEditOrganization } from "modules/management/organizationPermissions";
|
import { canEditOrganization } from "modules/permissions/organizations";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { Navigate } from "react-router-dom";
|
import { Navigate } from "react-router-dom";
|
||||||
|
|
||||||
@ -10,19 +10,25 @@ const OrganizationRedirect: FC = () => {
|
|||||||
organizationPermissionsByOrganizationId: organizationPermissions,
|
organizationPermissionsByOrganizationId: organizationPermissions,
|
||||||
} = useOrganizationSettings();
|
} = useOrganizationSettings();
|
||||||
|
|
||||||
|
const sortedOrganizations = [...organizations].sort(
|
||||||
|
(a, b) => (b.is_default ? 1 : 0) - (a.is_default ? 1 : 0),
|
||||||
|
);
|
||||||
|
|
||||||
// Redirect /organizations => /organizations/some-organization-name
|
// Redirect /organizations => /organizations/some-organization-name
|
||||||
// If they can edit the default org, we should redirect to the default.
|
// If they can edit the default org, we should redirect to the default.
|
||||||
// If they cannot edit the default, we should redirect to the first org that
|
// If they cannot edit the default, we should redirect to the first org that
|
||||||
// they can edit.
|
// they can edit.
|
||||||
const editableOrg = [...organizations]
|
const editableOrg = sortedOrganizations.find((org) =>
|
||||||
.sort((a, b) => (b.is_default ? 1 : 0) - (a.is_default ? 1 : 0))
|
canEditOrganization(organizationPermissions[org.id]),
|
||||||
.find((org) => canEditOrganization(organizationPermissions[org.id]));
|
);
|
||||||
if (editableOrg) {
|
if (editableOrg) {
|
||||||
return <Navigate to={`/organizations/${editableOrg.name}`} replace />;
|
return <Navigate to={`/organizations/${editableOrg.name}`} replace />;
|
||||||
}
|
}
|
||||||
// If they cannot edit any org, just redirect to an org they can read.
|
// If they cannot edit any org, just redirect to an org they can read.
|
||||||
if (organizations.length > 0) {
|
if (sortedOrganizations.length > 0) {
|
||||||
return <Navigate to={`/organizations/${organizations[0].name}`} replace />;
|
return (
|
||||||
|
<Navigate to={`/organizations/${sortedOrganizations[0].name}`} replace />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <EmptyState message="No organizations found" />;
|
return <EmptyState message="No organizations found" />;
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import { workspaceByOwnerAndNameKey } from "api/queries/workspaces";
|
|||||||
import type { Workspace, WorkspaceAgentLifecycle } from "api/typesGenerated";
|
import type { Workspace, WorkspaceAgentLifecycle } from "api/typesGenerated";
|
||||||
import { AuthProvider } from "contexts/auth/AuthProvider";
|
import { AuthProvider } from "contexts/auth/AuthProvider";
|
||||||
import { RequireAuth } from "contexts/auth/RequireAuth";
|
import { RequireAuth } from "contexts/auth/RequireAuth";
|
||||||
import { permissionChecks } from "contexts/auth/permissions";
|
import { permissionChecks } from "modules/permissions";
|
||||||
import {
|
import {
|
||||||
reactRouterOutlet,
|
reactRouterOutlet,
|
||||||
reactRouterParameters,
|
reactRouterParameters,
|
||||||
|
@ -40,7 +40,7 @@ const meta = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
user: MockUser,
|
user: MockUser,
|
||||||
permissions: { viewDeploymentValues: true },
|
permissions: { viewDeploymentConfig: true },
|
||||||
},
|
},
|
||||||
decorators: [withGlobalSnackbar, withAuthProvider, withDashboardProvider],
|
decorators: [withGlobalSnackbar, withAuthProvider, withDashboardProvider],
|
||||||
} satisfies Meta<typeof NotificationsPage>;
|
} satisfies Meta<typeof NotificationsPage>;
|
||||||
@ -74,7 +74,7 @@ export const ToggleNotification: Story = {
|
|||||||
|
|
||||||
export const NonAdmin: Story = {
|
export const NonAdmin: Story = {
|
||||||
parameters: {
|
parameters: {
|
||||||
permissions: { viewDeploymentValues: false },
|
permissions: { viewDeploymentConfig: false },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ export const NotificationsPage: FC = () => {
|
|||||||
...systemNotificationTemplates(),
|
...systemNotificationTemplates(),
|
||||||
select: (data: NotificationTemplate[]) => {
|
select: (data: NotificationTemplate[]) => {
|
||||||
const groups = selectTemplatesByGroup(data);
|
const groups = selectTemplatesByGroup(data);
|
||||||
return permissions.viewDeploymentValues
|
return permissions.viewDeploymentConfig
|
||||||
? groups
|
? groups
|
||||||
: {
|
: {
|
||||||
// Members only have access to the "Workspace Notifications" group
|
// Members only have access to the "Workspace Notifications" group
|
||||||
|
@ -63,7 +63,7 @@ const parameters = {
|
|||||||
permissions: {
|
permissions: {
|
||||||
createUser: true,
|
createUser: true,
|
||||||
updateUsers: true,
|
updateUsers: true,
|
||||||
viewDeploymentValues: true,
|
viewDeploymentConfig: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,12 +51,12 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
|
|||||||
const {
|
const {
|
||||||
createUser: canCreateUser,
|
createUser: canCreateUser,
|
||||||
updateUsers: canEditUsers,
|
updateUsers: canEditUsers,
|
||||||
viewDeploymentValues,
|
viewDeploymentConfig,
|
||||||
} = permissions;
|
} = permissions;
|
||||||
const rolesQuery = useQuery(roles());
|
const rolesQuery = useQuery(roles());
|
||||||
const { data: deploymentValues } = useQuery({
|
const { data: deploymentValues } = useQuery({
|
||||||
...deploymentConfig(),
|
...deploymentConfig(),
|
||||||
enabled: viewDeploymentValues,
|
enabled: viewDeploymentConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
const usersQuery = usePaginatedQuery(paginatedUsers(searchParamsResult[0]));
|
const usersQuery = usePaginatedQuery(paginatedUsers(searchParamsResult[0]));
|
||||||
@ -94,7 +94,7 @@ const UsersPage: FC<UserPageProps> = ({ defaultNewPassword }) => {
|
|||||||
// Indicates if oidc roles are synced from the oidc idp.
|
// Indicates if oidc roles are synced from the oidc idp.
|
||||||
// Assign 'false' if unknown.
|
// Assign 'false' if unknown.
|
||||||
const oidcRoleSyncEnabled =
|
const oidcRoleSyncEnabled =
|
||||||
viewDeploymentValues &&
|
viewDeploymentConfig &&
|
||||||
deploymentValues?.config.oidc?.user_role_field !== "";
|
deploymentValues?.config.oidc?.user_role_field !== "";
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
|
@ -11,7 +11,7 @@ const permissions: WorkspacePermissions = {
|
|||||||
readWorkspace: true,
|
readWorkspace: true,
|
||||||
updateWorkspace: true,
|
updateWorkspace: true,
|
||||||
updateTemplate: true,
|
updateTemplate: true,
|
||||||
viewDeploymentValues: true,
|
viewDeploymentConfig: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const meta: Meta<typeof Workspace> = {
|
const meta: Meta<typeof Workspace> = {
|
||||||
|
@ -15,7 +15,7 @@ const defaultPermissions = {
|
|||||||
readWorkspace: true,
|
readWorkspace: true,
|
||||||
updateTemplate: true,
|
updateTemplate: true,
|
||||||
updateWorkspace: true,
|
updateWorkspace: true,
|
||||||
viewDeploymentValues: true,
|
viewDeploymentConfig: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const meta: Meta<typeof WorkspaceNotifications> = {
|
const meta: Meta<typeof WorkspaceNotifications> = {
|
||||||
|
@ -66,7 +66,7 @@ export const WorkspaceReadyPage: FC<WorkspaceReadyPageProps> = ({
|
|||||||
// Debug mode
|
// Debug mode
|
||||||
const { data: deploymentValues } = useQuery({
|
const { data: deploymentValues } = useQuery({
|
||||||
...deploymentConfig(),
|
...deploymentConfig(),
|
||||||
enabled: permissions.viewDeploymentValues,
|
enabled: permissions.viewDeploymentConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build logs
|
// Build logs
|
||||||
|
@ -25,7 +25,7 @@ export const workspaceChecks = (workspace: Workspace, template: Template) =>
|
|||||||
},
|
},
|
||||||
action: "update",
|
action: "update",
|
||||||
},
|
},
|
||||||
viewDeploymentValues: {
|
viewDeploymentConfig: {
|
||||||
object: {
|
object: {
|
||||||
resource_type: "deployment_config",
|
resource_type: "deployment_config",
|
||||||
},
|
},
|
||||||
|
@ -156,7 +156,7 @@ const useWorkspacesFilter = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { permissions } = useAuthenticated();
|
const { permissions } = useAuthenticated();
|
||||||
const canFilterByUser = permissions.viewDeploymentValues;
|
const canFilterByUser = permissions.viewDeploymentConfig;
|
||||||
const userMenu = useUserFilterMenu({
|
const userMenu = useUserFilterMenu({
|
||||||
value: filter.values.owner,
|
value: filter.values.owner,
|
||||||
onChange: (option) =>
|
onChange: (option) =>
|
||||||
|
@ -31,8 +31,8 @@ const NotFoundPage = lazy(() => import("./pages/404Page/404Page"));
|
|||||||
const DeploymentSettingsLayout = lazy(
|
const DeploymentSettingsLayout = lazy(
|
||||||
() => import("./modules/management/DeploymentSettingsLayout"),
|
() => import("./modules/management/DeploymentSettingsLayout"),
|
||||||
);
|
);
|
||||||
const DeploymentSettingsProvider = lazy(
|
const DeploymentConfigProvider = lazy(
|
||||||
() => import("./modules/management/DeploymentSettingsProvider"),
|
() => import("./modules/management/DeploymentConfigProvider"),
|
||||||
);
|
);
|
||||||
const OrganizationSidebarLayout = lazy(
|
const OrganizationSidebarLayout = lazy(
|
||||||
() => import("./modules/management/OrganizationSidebarLayout"),
|
() => import("./modules/management/OrganizationSidebarLayout"),
|
||||||
@ -98,11 +98,8 @@ const TemplateSummaryPage = lazy(
|
|||||||
const CreateWorkspacePage = lazy(
|
const CreateWorkspacePage = lazy(
|
||||||
() => import("./pages/CreateWorkspacePage/CreateWorkspacePage"),
|
() => import("./pages/CreateWorkspacePage/CreateWorkspacePage"),
|
||||||
);
|
);
|
||||||
const GeneralSettingsPage = lazy(
|
const OverviewPage = lazy(
|
||||||
() =>
|
() => import("./pages/DeploymentSettingsPage/OverviewPage/OverviewPage"),
|
||||||
import(
|
|
||||||
"./pages/DeploymentSettingsPage/GeneralSettingsPage/GeneralSettingsPage"
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
const SecuritySettingsPage = lazy(
|
const SecuritySettingsPage = lazy(
|
||||||
() =>
|
() =>
|
||||||
@ -435,8 +432,8 @@ export const router = createBrowserRouter(
|
|||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path="/deployment" element={<DeploymentSettingsLayout />}>
|
<Route path="/deployment" element={<DeploymentSettingsLayout />}>
|
||||||
<Route element={<DeploymentSettingsProvider />}>
|
<Route element={<DeploymentConfigProvider />}>
|
||||||
<Route path="general" element={<GeneralSettingsPage />} />
|
<Route path="overview" element={<OverviewPage />} />
|
||||||
<Route path="security" element={<SecuritySettingsPage />} />
|
<Route path="security" element={<SecuritySettingsPage />} />
|
||||||
<Route
|
<Route
|
||||||
path="observability"
|
path="observability"
|
||||||
|
@ -5,10 +5,10 @@ import {
|
|||||||
} from "api/api";
|
} from "api/api";
|
||||||
import type { FieldError } from "api/errors";
|
import type { FieldError } from "api/errors";
|
||||||
import type * as TypesGen from "api/typesGenerated";
|
import type * as TypesGen from "api/typesGenerated";
|
||||||
import type { Permissions } from "contexts/auth/permissions";
|
|
||||||
import type { ProxyLatencyReport } from "contexts/useProxyLatency";
|
import type { ProxyLatencyReport } from "contexts/useProxyLatency";
|
||||||
import range from "lodash/range";
|
import range from "lodash/range";
|
||||||
import type { OrganizationPermissions } from "modules/management/organizationPermissions";
|
import type { Permissions } from "modules/permissions";
|
||||||
|
import type { OrganizationPermissions } from "modules/permissions/organizations";
|
||||||
import type { FileTree } from "utils/filetree";
|
import type { FileTree } from "utils/filetree";
|
||||||
import type { TemplateVersionFiles } from "utils/templateVersion";
|
import type { TemplateVersionFiles } from "utils/templateVersion";
|
||||||
|
|
||||||
@ -2844,11 +2844,9 @@ export const MockPermissions: Permissions = {
|
|||||||
viewAllUsers: true,
|
viewAllUsers: true,
|
||||||
updateUsers: true,
|
updateUsers: true,
|
||||||
viewAnyAuditLog: true,
|
viewAnyAuditLog: true,
|
||||||
viewDeploymentValues: true,
|
viewDeploymentConfig: true,
|
||||||
editDeploymentValues: true,
|
editDeploymentConfig: true,
|
||||||
viewUpdateCheck: true,
|
|
||||||
viewDeploymentStats: true,
|
viewDeploymentStats: true,
|
||||||
viewExternalAuthConfig: true,
|
|
||||||
readWorkspaceProxies: true,
|
readWorkspaceProxies: true,
|
||||||
editWorkspaceProxies: true,
|
editWorkspaceProxies: true,
|
||||||
createOrganization: true,
|
createOrganization: true,
|
||||||
@ -2873,11 +2871,9 @@ export const MockNoPermissions: Permissions = {
|
|||||||
viewAllUsers: false,
|
viewAllUsers: false,
|
||||||
updateUsers: false,
|
updateUsers: false,
|
||||||
viewAnyAuditLog: false,
|
viewAnyAuditLog: false,
|
||||||
viewDeploymentValues: false,
|
viewDeploymentConfig: false,
|
||||||
editDeploymentValues: false,
|
editDeploymentConfig: false,
|
||||||
viewUpdateCheck: false,
|
|
||||||
viewDeploymentStats: false,
|
viewDeploymentStats: false,
|
||||||
viewExternalAuthConfig: false,
|
|
||||||
readWorkspaceProxies: false,
|
readWorkspaceProxies: false,
|
||||||
editWorkspaceProxies: false,
|
editWorkspaceProxies: false,
|
||||||
createOrganization: false,
|
createOrganization: false,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { CreateWorkspaceBuildRequest } from "api/typesGenerated";
|
import type { CreateWorkspaceBuildRequest } from "api/typesGenerated";
|
||||||
import { permissionChecks } from "contexts/auth/permissions";
|
import { permissionChecks } from "modules/permissions";
|
||||||
import { http, HttpResponse } from "msw";
|
import { http, HttpResponse } from "msw";
|
||||||
import * as M from "./entities";
|
import * as M from "./entities";
|
||||||
import { MockGroup, MockWorkspaceQuota } from "./entities";
|
import { MockGroup, MockWorkspaceQuota } from "./entities";
|
||||||
|
@ -6,10 +6,10 @@ import { hasFirstUserKey, meKey } from "api/queries/users";
|
|||||||
import type { Entitlements } from "api/typesGenerated";
|
import type { Entitlements } from "api/typesGenerated";
|
||||||
import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar";
|
import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar";
|
||||||
import { AuthProvider } from "contexts/auth/AuthProvider";
|
import { AuthProvider } from "contexts/auth/AuthProvider";
|
||||||
import { permissionChecks } from "contexts/auth/permissions";
|
|
||||||
import { DashboardContext } from "modules/dashboard/DashboardProvider";
|
import { DashboardContext } from "modules/dashboard/DashboardProvider";
|
||||||
import { DeploymentSettingsContext } from "modules/management/DeploymentSettingsProvider";
|
import { DeploymentConfigContext } from "modules/management/DeploymentConfigProvider";
|
||||||
import { OrganizationSettingsContext } from "modules/management/OrganizationSettingsLayout";
|
import { OrganizationSettingsContext } from "modules/management/OrganizationSettingsLayout";
|
||||||
|
import { permissionChecks } from "modules/permissions";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { useQueryClient } from "react-query";
|
import { useQueryClient } from "react-query";
|
||||||
import {
|
import {
|
||||||
@ -168,11 +168,11 @@ export const withOrganizationSettingsProvider = (Story: FC) => {
|
|||||||
organizationPermissions: MockOrganizationPermissions,
|
organizationPermissions: MockOrganizationPermissions,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeploymentSettingsContext.Provider
|
<DeploymentConfigContext.Provider
|
||||||
value={{ deploymentConfig: MockDeploymentConfig }}
|
value={{ deploymentConfig: MockDeploymentConfig }}
|
||||||
>
|
>
|
||||||
<Story />
|
<Story />
|
||||||
</DeploymentSettingsContext.Provider>
|
</DeploymentConfigContext.Provider>
|
||||||
</OrganizationSettingsContext.Provider>
|
</OrganizationSettingsContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user