Skip to content

feat: unify organization and deployment management settings #13602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 1, 2024
Next Next commit
something
  • Loading branch information
aslilac committed Jun 18, 2024
commit e3da0a4507585e24f74bb8a25278d969d0ef528e
18 changes: 16 additions & 2 deletions site/src/pages/DeploySettingsPage/DeploySettingsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ 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 { Sidebar } from "./Sidebar";
import { Sidebar } from "../ManagementSettingsPage/Sidebar";
import { useDashboard } from "modules/dashboard/useDashboard";
import { ManagementSettingsLayout } from "pages/ManagementSettingsPage/ManagementSettingsLayout";

type DeploySettingsContextValue = {
deploymentValues: DeploymentConfig;
};

const DeploySettingsContext = createContext<
export const DeploySettingsContext = createContext<
DeploySettingsContextValue | undefined
>(undefined);

Expand All @@ -29,6 +31,18 @@ export const useDeploySettings = (): DeploySettingsContextValue => {
};

export const DeploySettingsLayout: FC = () => {
const { experiments } = useDashboard();

const multiOrgExperimentEnabled = experiments.includes("multi-organization");

return multiOrgExperimentEnabled ? (
<ManagementSettingsLayout />
) : (
<DeploySettingsLayoutInner />
);
};

const DeploySettingsLayoutInner: FC = () => {
const deploymentConfigQuery = useQuery(deploymentConfig());
const { permissions } = useAuthenticated();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { RequirePermission } from "contexts/auth/RequirePermission";
import { useDashboard } from "modules/dashboard/useDashboard";
import NotFoundPage from "pages/404Page/404Page";
import { Sidebar } from "./Sidebar";
import { deploymentConfig } from "api/queries/deployment";
import { DeploySettingsContext } from "../DeploySettingsPage/DeploySettingsLayout";

type OrganizationSettingsContextValue = {
currentOrganizationId: string;
currentOrganizationId?: string;
organizations: Organization[];
};

Expand All @@ -31,10 +33,11 @@ export const useOrganizationSettings = (): OrganizationSettingsContextValue => {
return context;
};

export const OrganizationSettingsLayout: FC = () => {
export const ManagementSettingsLayout: FC = () => {
const { permissions, organizationIds } = useAuthenticated();
const { experiments } = useDashboard();
const { organization } = useParams() as { organization: string };
const deploymentConfigQuery = useQuery(deploymentConfig());
const organizationsQuery = useQuery(myOrganizations());

const multiOrgExperimentEnabled = experiments.includes("multi-organization");
Expand All @@ -50,18 +53,29 @@ export const OrganizationSettingsLayout: FC = () => {
{organizationsQuery.data ? (
<OrganizationSettingsContext.Provider
value={{
currentOrganizationId:
organizationsQuery.data.find(
(org) => org.name === organization,
)?.id ?? organizationIds[0],
currentOrganizationId: !organization
? organizationIds[0]
: organizationsQuery.data.find(
(org) => org.name === organization,
)?.id,
organizations: organizationsQuery.data,
}}
>
<Sidebar />
<main css={{ width: "100%" }}>
<Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
{deploymentConfigQuery.data ? (
<DeploySettingsContext.Provider
value={{
deploymentValues: deploymentConfigQuery.data,
}}
>
<Suspense fallback={<Loader />}>
<Outlet />
</Suspense>
</DeploySettingsContext.Provider>
) : (
<Loader />
)}
</main>
</OrganizationSettingsContext.Provider>
) : (
Expand Down
71 changes: 71 additions & 0 deletions site/src/pages/ManagementSettingsPage/OrganizationSettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { type FC } from "react";
import { useMutation, useQueryClient } from "react-query";
import { useNavigate } from "react-router-dom";
import {
createOrganization,
updateOrganization,
deleteOrganization,
} from "api/queries/organizations";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { Stack } from "components/Stack/Stack";
import { useOrganizationSettings } from "./ManagementSettingsLayout";
import { OrganizationSettingsPageView } from "./OrganizationSettingsPageView";

const OrganizationSettingsPage: FC = () => {
const navigate = useNavigate();

const queryClient = useQueryClient();
const addOrganizationMutation = useMutation(createOrganization(queryClient));
const updateOrganizationMutation = useMutation(
updateOrganization(queryClient),
);
const deleteOrganizationMutation = useMutation(
deleteOrganization(queryClient),
);

const { currentOrganizationId, organizations } = useOrganizationSettings();

const org = organizations.find((org) => org.id === currentOrganizationId);

const error =
updateOrganizationMutation.error ??
addOrganizationMutation.error ??
deleteOrganizationMutation.error;

if (!currentOrganizationId) {
return null;
}

if (!org) {
return null;
}

return (
<Stack>
{Boolean(error) && <ErrorAlert error={error} />}

<OrganizationSettingsPageView
org={org}
error={error}
onSubmit={async (values) => {
await updateOrganizationMutation.mutateAsync({
orgId: org.id,
req: values,
});
displaySuccess("Organization settings updated.");
}}
onCreateOrg={(name) => {
addOrganizationMutation.mutate({ name });
navigate(`/organizations/${name}`);
}}
onDeleteOrg={() => {
deleteOrganizationMutation.mutate(org.id);
navigate("/organizations");
}}
/>
</Stack>
);
};

export default OrganizationSettingsPage;
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
updateOrganization,
deleteOrganization,
} from "api/queries/organizations";
import type { UpdateOrganizationRequest } from "api/typesGenerated";
import type {
Organization,
UpdateOrganizationRequest,
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import {
FormFields,
Expand All @@ -29,12 +32,12 @@ import {
displayNameValidator,
onChangeTrimmed,
} from "utils/formUtils";
import { useOrganizationSettings } from "./OrganizationSettingsLayout";
import { useOrganizationSettings } from "./ManagementSettingsLayout";

const MAX_DESCRIPTION_CHAR_LIMIT = 128;
const MAX_DESCRIPTION_MESSAGE = `Please enter a description that is no longer than ${MAX_DESCRIPTION_CHAR_LIMIT} characters.`;

export const validationSchema = Yup.object({
const validationSchema = Yup.object({
name: nameValidator("Name"),
display_name: displayNameValidator("Display name"),
description: Yup.string().max(
Expand All @@ -43,25 +46,18 @@ export const validationSchema = Yup.object({
),
});

const OrganizationSettingsPage: FC = () => {
const queryClient = useQueryClient();
const addOrganizationMutation = useMutation(createOrganization(queryClient));
const updateOrganizationMutation = useMutation(
updateOrganization(queryClient),
);
const deleteOrganizationMutation = useMutation(
deleteOrganization(queryClient),
);

const { currentOrganizationId, organizations } = useOrganizationSettings();
interface OrganizationSettingsPageViewProps {
org: Organization;
error: unknown;
onSubmit: (values: UpdateOrganizationRequest) => Promise<void>;

const org = organizations.find((org) => org.id === currentOrganizationId)!;

const error =
updateOrganizationMutation.error ??
addOrganizationMutation.error ??
deleteOrganizationMutation.error;
onCreateOrg: (name: string) => void;
onDeleteOrg: () => void;
}

export const OrganizationSettingsPageView: FC<
OrganizationSettingsPageViewProps
> = ({ org, error, onSubmit, onCreateOrg, onDeleteOrg }) => {
const form = useFormik<UpdateOrganizationRequest>({
initialValues: {
name: org.name,
Expand All @@ -70,24 +66,16 @@ const OrganizationSettingsPage: FC = () => {
icon: org.icon,
},
validationSchema,
onSubmit: async (values) => {
await updateOrganizationMutation.mutateAsync({
orgId: org.id,
req: values,
});
displaySuccess("Organization settings updated.");
},
onSubmit,
enableReinitialize: true,
});
const getFieldHelpers = getFormHelpers(form, error);

const [newOrgName, setNewOrgName] = useState("");

return (
<Margins css={{ marginTop: 18, marginBottom: 18 }}>
{Boolean(error) && <ErrorAlert error={error} />}

<PageHeader css={{ paddingTop: error ? undefined : 0 }}>
<div>
<PageHeader>
<PageHeaderTitle>Organization settings</PageHeaderTitle>
</PageHeader>

Expand Down Expand Up @@ -139,9 +127,7 @@ const OrganizationSettingsPage: FC = () => {
<Button
css={styles.dangerButton}
variant="contained"
onClick={() =>
deleteOrganizationMutation.mutate(currentOrganizationId)
}
onClick={onDeleteOrg}
>
Delete this organization
</Button>
Expand All @@ -152,18 +138,14 @@ const OrganizationSettingsPage: FC = () => {
label="New organization name"
onChange={(event) => setNewOrgName(event.target.value)}
/>
<Button
onClick={() => addOrganizationMutation.mutate({ name: newOrgName })}
>
<Button onClick={() => onCreateOrg(newOrgName)}>
Create new organization
</Button>
</Stack>
</Margins>
</div>
);
};

export default OrganizationSettingsPage;

const styles = {
dangerButton: (theme) => ({
"&.MuiButton-contained": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from "api/queries/organizations";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Margins } from "components/Margins/Margins";
import { useOrganizationSettings } from "./OrganizationSettingsLayout";
import { useOrganizationSettings } from "./ManagementSettingsLayout";

const OrganizationSettingsPage: FC = () => {
const queryClient = useQueryClient();
Expand Down
Loading