-
Notifications
You must be signed in to change notification settings - Fork 894
chore: consolidate ManageSettingsLayout
code
#14885
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
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
bf9ab32
typescript is so irritating sometimes
aslilac 05d1e80
oh
aslilac 602ffab
fmt
aslilac 88bc231
Merge branch 'main' into lilac/anger
aslilac 445b82e
yay
aslilac 183c5db
Merge branch 'main' into lilac/anger
aslilac c8eaa77
oih
aslilac f4beba0
here
aslilac 3af9601
fix route
aslilac 16f6650
Merge branch 'main' into lilac/anger
aslilac 932f829
:)
aslilac 4288004
whoops
aslilac 62b4575
mock the context
aslilac 6581e8d
fix some stories
aslilac 0719fbd
jerk
aslilac 6fea1be
wow
aslilac fc7732c
そうそう
aslilac File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
typescript is so irritating sometimes
- Loading branch information
commit bf9ab326fa9417d03b9c24020ad31c1fce3bd7a5
There are no files selected for viewing
111 changes: 111 additions & 0 deletions
111
site/src/modules/management/ManagementSettingsLayout.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { deploymentConfig } from "api/queries/deployment"; | ||
import type { | ||
AuthorizationResponse, | ||
DeploymentConfig, | ||
Organization, | ||
} from "api/typesGenerated"; | ||
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 { createContext, type FC, Suspense, useContext } from "react"; | ||
import { useQuery } from "react-query"; | ||
import { Navigate, Outlet, useNavigate, useParams } from "react-router-dom"; | ||
import { Sidebar } from "./Sidebar"; | ||
import { ErrorAlert } from "components/Alert/ErrorAlert"; | ||
|
||
export const ManagementSettingsContext = createContext< | ||
{ deploymentValues: DeploymentConfig } | undefined | ||
>(undefined); | ||
|
||
type ManagementSettingsValue = Readonly<{ | ||
deploymentValues: DeploymentConfig; | ||
organizations: readonly Organization[]; | ||
organization?: Organization; | ||
}>; | ||
|
||
export const useManagementSettings = (): ManagementSettingsValue => { | ||
const context = useContext(ManagementSettingsContext); | ||
if (!context) { | ||
throw new Error( | ||
"useManagementSettings should be used inside of ManagementSettingsLayout", | ||
); | ||
} | ||
const { organizations } = useDashboard(); | ||
const { organization: orgName } = useParams() as { | ||
organization?: string; | ||
}; | ||
|
||
const organization = | ||
organizations && orgName | ||
? organizations.find((org) => org.name === orgName) | ||
: undefined; | ||
|
||
return { | ||
deploymentValues: context.deploymentValues, | ||
organizations, | ||
organization, | ||
}; | ||
}; | ||
|
||
/** | ||
* Return true if the user can edit the organization settings or its members. | ||
*/ | ||
export const canEditOrganization = ( | ||
permissions: AuthorizationResponse | undefined, | ||
) => { | ||
return ( | ||
permissions !== undefined && | ||
(permissions.editOrganization || | ||
permissions.editMembers || | ||
permissions.editGroups) | ||
); | ||
}; | ||
|
||
/** | ||
* A multi-org capable settings page layout. | ||
* | ||
* If multi-org is not enabled or licensed, this is the wrong layout to use. | ||
* See DeploySettingsLayoutInner instead. | ||
*/ | ||
export const ManagementSettingsLayout: FC = () => { | ||
const { permissions } = useAuthenticated(); | ||
const deploymentConfigQuery = useQuery(deploymentConfig()); | ||
|
||
// 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. | ||
const canViewDeploymentSettingsPage = | ||
permissions.viewDeploymentValues || | ||
permissions.viewAllUsers || | ||
permissions.editAnyOrganization || | ||
permissions.viewAnyAuditLog; | ||
|
||
if (deploymentConfigQuery.error) { | ||
return <ErrorAlert error={deploymentConfigQuery.error} />; | ||
} | ||
|
||
if (!deploymentConfigQuery.data) { | ||
return <Loader />; | ||
} | ||
|
||
return ( | ||
<Margins> | ||
<Stack css={{ padding: "48px 0" }} direction="row" spacing={6}> | ||
<Sidebar /> | ||
<main css={{ width: "100%" }}> | ||
<Suspense fallback={<Loader />}> | ||
<RequirePermission isFeatureVisible={canViewDeploymentSettingsPage}> | ||
<ManagementSettingsContext.Provider | ||
value={{ deploymentValues: deploymentConfigQuery.data }} | ||
> | ||
<Outlet /> | ||
</ManagementSettingsContext.Provider> | ||
</RequirePermission> | ||
</Suspense> | ||
</main> | ||
</Stack> | ||
</Margins> | ||
); | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { organizationsPermissions } from "api/queries/organizations"; | ||
import { useAuthenticated } from "contexts/auth/RequireAuth"; | ||
import { useDashboard } from "modules/dashboard/useDashboard"; | ||
import type { FC } from "react"; | ||
import { useQuery } from "react-query"; | ||
import { useLocation, useParams } from "react-router-dom"; | ||
import { | ||
canEditOrganization, | ||
useManagementSettings, | ||
} from "modules/management/ManagementSettingsLayout"; | ||
import { type OrganizationWithPermissions, SidebarView } from "./SidebarView"; | ||
|
||
/** | ||
* A combined deployment settings and organization menu. | ||
* | ||
* This should only be used with multi-org support. If multi-org support is | ||
* disabled or not licensed, this is the wrong sidebar to use. See | ||
* DeploySettingsPage/Sidebar instead. | ||
*/ | ||
export const Sidebar: FC = () => { | ||
const location = useLocation(); | ||
const { permissions } = useAuthenticated(); | ||
const { experiments } = useDashboard(); | ||
const { organizations } = useManagementSettings(); | ||
const { organization: organizationName } = useParams() as { | ||
organization?: string; | ||
}; | ||
|
||
const orgPermissionsQuery = useQuery( | ||
organizationsPermissions(organizations?.map((o) => o.id)), | ||
); | ||
|
||
// Sometimes a user can read an organization but cannot actually do anything | ||
// with it. For now, these are filtered out so you only see organizations you | ||
// can manage in some way. | ||
const editableOrgs = organizations | ||
?.map((org) => { | ||
return { | ||
...org, | ||
permissions: orgPermissionsQuery.data?.[org.id], | ||
}; | ||
}) | ||
// TypeScript is not able to infer whether permissions are defined on the | ||
// object even if we explicitly check org.permissions here, so add the `is` | ||
// here to help out (canEditOrganization does the actual check). | ||
.filter((org): org is OrganizationWithPermissions => { | ||
return canEditOrganization(org.permissions); | ||
}); | ||
|
||
return ( | ||
<SidebarView | ||
// Both activeSettings and activeOrganizationName could be be falsey if | ||
// the user is on /organizations but has no editable organizations to | ||
// which we can redirect. | ||
activeSettings={location.pathname.startsWith("/deployment")} | ||
activeOrganizationName={organizationName} | ||
organizations={editableOrgs} | ||
permissions={permissions} | ||
experiments={experiments} | ||
/> | ||
); | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.