mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: Display member role when user has no role (#1965)
This commit is contained in:
@@ -41,7 +41,7 @@ export const RoleSelect: FC<RoleSelectProps> = ({ roles, selectedRoles, loading,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem key={r.name} value={r.name} disabled={loading}>
|
<MenuItem key={r.name} value={r.name} disabled={loading}>
|
||||||
<Checkbox color="primary" checked={isChecked} /> {r.display_name}
|
<Checkbox size="small" color="primary" checked={isChecked} /> {r.display_name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { screen } from "@testing-library/react"
|
import { screen } from "@testing-library/react"
|
||||||
import { MockAdminRole, MockMemberRole, MockUser } from "../../testHelpers/entities"
|
import { MockAdminRole, MockUser } from "../../testHelpers/entities"
|
||||||
import { render } from "../../testHelpers/renderHelpers"
|
import { render } from "../../testHelpers/renderHelpers"
|
||||||
import { Language, UserDropdown, UserDropdownProps } from "./UsersDropdown"
|
import { Language, UserDropdown, UserDropdownProps } from "./UsersDropdown"
|
||||||
|
|
||||||
@@ -36,7 +36,6 @@ describe("UserDropdown", () => {
|
|||||||
await renderAndClick()
|
await renderAndClick()
|
||||||
|
|
||||||
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
|
expect(screen.getByText(MockAdminRole.display_name)).toBeDefined()
|
||||||
expect(screen.getByText(MockMemberRole.display_name)).toBeDefined()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("has the correct link for the documentation item", async () => {
|
it("has the correct link for the documentation item", async () => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Box from "@material-ui/core/Box"
|
import Box from "@material-ui/core/Box"
|
||||||
|
import { makeStyles } from "@material-ui/core/styles"
|
||||||
import Table from "@material-ui/core/Table"
|
import Table from "@material-ui/core/Table"
|
||||||
import TableBody from "@material-ui/core/TableBody"
|
import TableBody from "@material-ui/core/TableBody"
|
||||||
import TableCell from "@material-ui/core/TableCell"
|
import TableCell from "@material-ui/core/TableCell"
|
||||||
@@ -6,6 +7,7 @@ import TableHead from "@material-ui/core/TableHead"
|
|||||||
import TableRow from "@material-ui/core/TableRow"
|
import TableRow from "@material-ui/core/TableRow"
|
||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
import * as TypesGen from "../../api/typesGenerated"
|
import * as TypesGen from "../../api/typesGenerated"
|
||||||
|
import { combineClasses } from "../../util/combineClasses"
|
||||||
import { AvatarData } from "../AvatarData/AvatarData"
|
import { AvatarData } from "../AvatarData/AvatarData"
|
||||||
import { EmptyState } from "../EmptyState/EmptyState"
|
import { EmptyState } from "../EmptyState/EmptyState"
|
||||||
import { RoleSelect } from "../RoleSelect/RoleSelect"
|
import { RoleSelect } from "../RoleSelect/RoleSelect"
|
||||||
@@ -45,6 +47,8 @@ export const UsersTable: FC<UsersTableProps> = ({
|
|||||||
canEditUsers,
|
canEditUsers,
|
||||||
isLoading,
|
isLoading,
|
||||||
}) => {
|
}) => {
|
||||||
|
const styles = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
@@ -60,55 +64,75 @@ export const UsersTable: FC<UsersTableProps> = ({
|
|||||||
{isLoading && <TableLoader />}
|
{isLoading && <TableLoader />}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
users &&
|
users &&
|
||||||
users.map((u) => (
|
users.map((user) => {
|
||||||
<TableRow key={u.id}>
|
// When the user has no role we want to show they are a Member
|
||||||
<TableCell>
|
const fallbackRole: TypesGen.Role = {
|
||||||
<AvatarData title={u.username} subtitle={u.email} />
|
name: "member",
|
||||||
</TableCell>
|
display_name: "Member",
|
||||||
<TableCell>{u.status}</TableCell>
|
}
|
||||||
<TableCell>
|
const userRoles = user.roles.length === 0 ? [fallbackRole] : user.roles
|
||||||
{canEditUsers ? (
|
|
||||||
<RoleSelect
|
return (
|
||||||
roles={roles ?? []}
|
<TableRow key={user.id}>
|
||||||
selectedRoles={u.roles}
|
|
||||||
loading={isUpdatingUserRoles}
|
|
||||||
onChange={(roles) => onUpdateUserRoles(u, roles)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>{u.roles.map((r) => r.display_name).join(", ")}</>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
{canEditUsers && (
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<TableRowMenu
|
<AvatarData title={user.username} subtitle={user.email} />
|
||||||
data={u}
|
|
||||||
menuItems={
|
|
||||||
// Return either suspend or activate depending on status
|
|
||||||
(u.status === "active"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
label: Language.suspendMenuItem,
|
|
||||||
onClick: onSuspendUser,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
// TODO: Uncomment this and add activate user functionality.
|
|
||||||
// {
|
|
||||||
// label: Language.activateMenuItem,
|
|
||||||
// // eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
// onClick: function () {},
|
|
||||||
// },
|
|
||||||
]
|
|
||||||
).concat({
|
|
||||||
label: Language.resetPasswordMenuItem,
|
|
||||||
onClick: onResetUserPassword,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
<TableCell
|
||||||
</TableRow>
|
className={combineClasses([
|
||||||
))}
|
styles.status,
|
||||||
|
user.status === "suspended" ? styles.suspended : undefined,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
{user.status}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{canEditUsers ? (
|
||||||
|
<RoleSelect
|
||||||
|
roles={roles ?? []}
|
||||||
|
selectedRoles={userRoles}
|
||||||
|
loading={isUpdatingUserRoles}
|
||||||
|
onChange={(roles) => {
|
||||||
|
// Remove the fallback role because it is only for the UI
|
||||||
|
roles = roles.filter((role) => role !== fallbackRole.name)
|
||||||
|
onUpdateUserRoles(user, roles)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>{userRoles.map((role) => role.display_name).join(", ")}</>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
{canEditUsers && (
|
||||||
|
<TableCell>
|
||||||
|
<TableRowMenu
|
||||||
|
data={user}
|
||||||
|
menuItems={
|
||||||
|
// Return either suspend or activate depending on status
|
||||||
|
(user.status === "active"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: Language.suspendMenuItem,
|
||||||
|
onClick: onSuspendUser,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
// TODO: Uncomment this and add activate user functionality.
|
||||||
|
// {
|
||||||
|
// label: Language.activateMenuItem,
|
||||||
|
// // eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
// onClick: function () {},
|
||||||
|
// },
|
||||||
|
]
|
||||||
|
).concat({
|
||||||
|
label: Language.resetPasswordMenuItem,
|
||||||
|
onClick: onResetUserPassword,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
{users && users.length === 0 && (
|
{users && users.length === 0 && (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -123,3 +147,12 @@ export const UsersTable: FC<UsersTableProps> = ({
|
|||||||
</Table>
|
</Table>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
status: {
|
||||||
|
textTransform: "capitalize",
|
||||||
|
},
|
||||||
|
suspended: {
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ describe("Users Page", () => {
|
|||||||
}, MockAuditorRole)
|
}, MockAuditorRole)
|
||||||
|
|
||||||
// Check if the select text was updated with the Auditor role
|
// Check if the select text was updated with the Auditor role
|
||||||
await waitFor(() => expect(rolesMenuTrigger).toHaveTextContent("Admin, Member, Auditor"))
|
await waitFor(() => expect(rolesMenuTrigger).toHaveTextContent("Admin, Auditor"))
|
||||||
|
|
||||||
// Check if the API was called correctly
|
// Check if the API was called correctly
|
||||||
const currentRoles = MockUser.roles.map((r) => r.name)
|
const currentRoles = MockUser.roles.map((r) => r.name)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const MockAuditorRole: TypesGen.Role = {
|
|||||||
display_name: "Auditor",
|
display_name: "Auditor",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MockSiteRoles = [MockAdminRole, MockAuditorRole, MockMemberRole]
|
export const MockSiteRoles = [MockAdminRole, MockAuditorRole]
|
||||||
|
|
||||||
export const MockUser: TypesGen.User = {
|
export const MockUser: TypesGen.User = {
|
||||||
id: "test-user",
|
id: "test-user",
|
||||||
@@ -38,7 +38,7 @@ export const MockUser: TypesGen.User = {
|
|||||||
created_at: "",
|
created_at: "",
|
||||||
status: "active",
|
status: "active",
|
||||||
organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"],
|
organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"],
|
||||||
roles: [MockAdminRole, MockMemberRole],
|
roles: [MockAdminRole],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MockUser2: TypesGen.User = {
|
export const MockUser2: TypesGen.User = {
|
||||||
@@ -48,7 +48,7 @@ export const MockUser2: TypesGen.User = {
|
|||||||
created_at: "",
|
created_at: "",
|
||||||
status: "active",
|
status: "active",
|
||||||
organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"],
|
organization_ids: ["fc0774ce-cc9e-48d4-80ae-88f7a4d4a8b0"],
|
||||||
roles: [MockMemberRole],
|
roles: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MockOrganization: TypesGen.Organization = {
|
export const MockOrganization: TypesGen.Organization = {
|
||||||
|
|||||||
Reference in New Issue
Block a user