Skip to content

Commit 615bb94

Browse files
authored
feat(site): embed users page in management settings (coder#14006)
1 parent 6161d17 commit 615bb94

File tree

7 files changed

+70
-12
lines changed

7 files changed

+70
-12
lines changed

site/src/pages/CreateUserPage/CreateUserPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export const CreateUserPage: FC = () => {
3232
onSubmit={async (user) => {
3333
await createUserMutation.mutateAsync(user);
3434
displaySuccess("Successfully created user.");
35-
navigate("/users");
35+
navigate("..", { relative: "path" });
3636
}}
3737
onCancel={() => {
38-
navigate("/users");
38+
navigate("..", { relative: "path" });
3939
}}
4040
isLoading={createUserMutation.isLoading}
4141
organizationId={organizationId}

site/src/pages/ManagementSettingsPage/Sidebar.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Sidebar as BaseSidebar } from "components/Sidebar/Sidebar";
99
import { Stack } from "components/Stack/Stack";
1010
import { UserAvatar } from "components/UserAvatar/UserAvatar";
1111
import { type ClassName, useClassName } from "hooks/useClassName";
12+
import { USERS_LINK } from "modules/navigation";
1213
import { useOrganizationSettings } from "./ManagementSettingsLayout";
1314

1415
export const Sidebar: FC = () => {
@@ -77,7 +78,9 @@ const DeploymentSettingsNavigation: FC = () => {
7778
<SidebarNavSubItem href="observability">
7879
Observability
7980
</SidebarNavSubItem>
80-
<SidebarNavSubItem href="/users">Users</SidebarNavSubItem>
81+
<SidebarNavSubItem href={USERS_LINK.slice(1)}>
82+
Users
83+
</SidebarNavSubItem>
8184
</Stack>
8285
)}
8386
</div>

site/src/pages/UsersPage/UsersLayout.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const UsersLayout: FC = () => {
2727
const location = useLocation();
2828
const activeTab = location.pathname.endsWith("groups") ? "groups" : "users";
2929

30+
const isMultiOrg = experiments.includes("multi-organization");
31+
3032
return (
3133
<>
3234
<Margins>
@@ -59,7 +61,7 @@ export const UsersLayout: FC = () => {
5961
</PageHeader>
6062
</Margins>
6163

62-
{!experiments.includes("multi-organization") && (
64+
{!isMultiOrg && (
6365
<Tabs
6466
css={{ marginBottom: 40, marginTop: -TAB_PADDING_Y }}
6567
active={activeTab}

site/src/pages/UsersPage/UsersPage.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { type FC, useState } from "react";
22
import { Helmet } from "react-helmet-async";
33
import { useMutation, useQuery, useQueryClient } from "react-query";
4-
import { useSearchParams, useNavigate } from "react-router-dom";
4+
import {
5+
useSearchParams,
6+
useNavigate,
7+
Navigate,
8+
useLocation,
9+
} from "react-router-dom";
510
import { getErrorMessage } from "api/errors";
611
import { deploymentConfig } from "api/queries/deployment";
712
import { groupsByUserId } from "api/queries/groups";
@@ -33,16 +38,21 @@ import { UsersPageView } from "./UsersPageView";
3338
const UsersPage: FC = () => {
3439
const queryClient = useQueryClient();
3540
const navigate = useNavigate();
36-
41+
const location = useLocation();
3742
const searchParamsResult = useSearchParams();
38-
const { entitlements, organizationId } = useDashboard();
43+
const { entitlements, experiments, organizationId } = useDashboard();
3944
const [searchParams] = searchParamsResult;
45+
const isMultiOrg = experiments.includes("multi-organization");
4046

4147
const groupsByUserIdQuery = useQuery(groupsByUserId(organizationId));
4248
const authMethodsQuery = useQuery(authMethods());
4349

4450
const { permissions, user: me } = useAuthenticated();
45-
const { updateUsers: canEditUsers, viewDeploymentValues } = permissions;
51+
const {
52+
createUser: canCreateUser,
53+
updateUsers: canEditUsers,
54+
viewDeploymentValues,
55+
} = permissions;
4656
const rolesQuery = useQuery(roles());
4757
const { data: deploymentValues } = useQuery({
4858
...deploymentConfig(),
@@ -93,6 +103,13 @@ const UsersPage: FC = () => {
93103
authMethodsQuery.isLoading ||
94104
groupsByUserIdQuery.isLoading;
95105

106+
if (
107+
experiments.includes("multi-organization") &&
108+
location.pathname !== "/deployment/users"
109+
) {
110+
return <Navigate to={`/deployment/users${location.search}`} replace />;
111+
}
112+
96113
return (
97114
<>
98115
<Helmet>
@@ -147,6 +164,8 @@ const UsersPage: FC = () => {
147164
menus: { status: statusMenu },
148165
}}
149166
usersQuery={usersQuery}
167+
isMultiOrg={isMultiOrg}
168+
canCreateUser={canCreateUser}
150169
/>
151170

152171
<DeleteDialog

site/src/pages/UsersPage/UsersPageView.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import PersonAdd from "@mui/icons-material/PersonAdd";
2+
import Button from "@mui/material/Button";
13
import type { ComponentProps, FC } from "react";
4+
import { useNavigate } from "react-router-dom";
25
import type { GroupsByUserId } from "api/queries/groups";
36
import type * as TypesGen from "api/typesGenerated";
7+
import { PageHeader, PageHeaderTitle } from "components/PageHeader/PageHeader";
48
import {
59
PaginationContainer,
610
type PaginationResult,
@@ -32,6 +36,10 @@ export interface UsersPageViewProps {
3236
actorID: string;
3337
groupsByUserId: GroupsByUserId | undefined;
3438
usersQuery: PaginationResult;
39+
40+
// TODO: Refactor these out once we remove the multi-organization experiment.
41+
isMultiOrg?: boolean;
42+
canCreateUser?: boolean;
3543
}
3644

3745
export const UsersPageView: FC<UsersPageViewProps> = ({
@@ -55,9 +63,33 @@ export const UsersPageView: FC<UsersPageViewProps> = ({
5563
authMethods,
5664
groupsByUserId,
5765
usersQuery,
66+
isMultiOrg,
67+
canCreateUser,
5868
}) => {
69+
const navigate = useNavigate();
70+
5971
return (
6072
<>
73+
{isMultiOrg && (
74+
<PageHeader
75+
css={{ paddingTop: 0 }}
76+
actions={
77+
<>
78+
{canCreateUser && (
79+
<Button
80+
onClick={() => navigate("create")}
81+
startIcon={<PersonAdd />}
82+
>
83+
Create user
84+
</Button>
85+
)}
86+
</>
87+
}
88+
>
89+
<PageHeaderTitle>Users</PageHeaderTitle>
90+
</PageHeader>
91+
)}
92+
6193
<UsersFilter {...filterProps} />
6294

6395
<PaginationContainer query={usersQuery} paginationUnitLabel="users">

site/src/pages/UsersPage/UsersTable/UsersTable.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const UsersTable: FC<UsersTableProps> = ({
6969
<Table data-testid="users-table">
7070
<TableHead>
7171
<TableRow>
72-
<TableCell width="29%">{Language.usernameLabel}</TableCell>
72+
<TableCell width="32%">{Language.usernameLabel}</TableCell>
7373

7474
<TableCell width="29%">
7575
<Stack direction="row" spacing={1} alignItems="center">
@@ -78,15 +78,15 @@ export const UsersTable: FC<UsersTableProps> = ({
7878
</Stack>
7979
</TableCell>
8080

81-
<TableCell width="14%">
81+
<TableCell width="13%">
8282
<Stack direction="row" spacing={1} alignItems="center">
8383
<span>{Language.groupsLabel}</span>
8484
<TableColumnHelpTooltip variant="groups" />
8585
</Stack>
8686
</TableCell>
8787

88-
<TableCell width="14%">{Language.loginTypeLabel}</TableCell>
89-
<TableCell width="14%">{Language.statusLabel}</TableCell>
88+
<TableCell width="13%">{Language.loginTypeLabel}</TableCell>
89+
<TableCell width="13%">{Language.statusLabel}</TableCell>
9090

9191
{/* 1% is a trick to make the table cell width fit the content */}
9292
{canEditUsers && <TableCell width="1%" />}

site/src/router.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ export const router = createBrowserRouter(
407407
</Route>
408408

409409
<Route path="workspace-proxies" element={<WorkspaceProxyPage />} />
410+
<Route path="users" element={<UsersPage />} />
411+
<Route path="users/create" element={<CreateUserPage />} />
410412
</Route>
411413

412414
<Route path="/settings" element={<UserSettingsLayout />}>

0 commit comments

Comments
 (0)