Skip to content

Commit d34bfb8

Browse files
committed
Implement adding and removing roles to and from a user
1 parent 3a293aa commit d34bfb8

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

src/contexts/api/auth/AuthContext.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ export interface AuthContextValue {
8989
/** Deletes a role */
9090
deleteRole(id: number): Promise<Result<void>>;
9191

92+
/** Adds a user to a role */
93+
addUserToRole(userid: number, roleid: number): Promise<Result<void>>;
94+
95+
/** Remove a user from a role */
96+
removeUserFromRole(userid: number, roleid: number): Promise<Result<void>>;
97+
9298
/** Fetches all registration keys */
9399
getAllRegistrationKeys(): Promise<Result<RegistrationKey[]>>;
94100

@@ -130,6 +136,10 @@ export const AuthContext = createContext<AuthContextValue>({
130136
createRole: async () => errorResult('No auth context for creating role'),
131137
updateRole: async () => errorResult('No auth context for updating role'),
132138
deleteRole: async () => errorResult('No auth context for deleting role'),
139+
addUserToRole: async () =>
140+
errorResult('No auth context for adding user to role'),
141+
removeUserFromRole: async () =>
142+
errorResult('No auth context for removing user from role'),
133143
getAllRegistrationKeys: async () =>
134144
errorResult('No auth context for fetching registration keys'),
135145
getRegistrationKeyById: async () =>
@@ -404,6 +414,28 @@ export function AuthContextProvider({ children }: AuthContextProviderProps) {
404414
}
405415
},
406416

417+
async addUserToRole(userid: number, roleid: number) {
418+
try {
419+
await apiRef.current.roles.usersUpdate(roleid, userid);
420+
return okResult(undefined);
421+
} catch (error) {
422+
return errorResult(
423+
`Adding user with id ${userid} to role ${roleid} failed: ${await formatError(error)}`
424+
);
425+
}
426+
},
427+
428+
async removeUserFromRole(userid: number, roleid: number) {
429+
try {
430+
await apiRef.current.roles.usersDelete(roleid, userid);
431+
return okResult(undefined);
432+
} catch (error) {
433+
return errorResult(
434+
`Removing user with id ${userid} from role ${roleid} failed: ${await formatError(error)}`
435+
);
436+
}
437+
},
438+
407439
async getAllRegistrationKeys() {
408440
try {
409441
const apiRegKeysResponse =

src/screens/home/admin/UsersView.tsx

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { User } from '@luna/contexts/api/auth/types';
1+
import { Role, User } from '@luna/contexts/api/auth/types';
22
import { UserAddModal } from '@luna/modals/UserAddModal';
33
import { UserDeleteModal } from '@luna/modals/UserDeleteModal';
44
import { UserDetailsModal } from '@luna/modals/UserDetailsModal';
@@ -9,6 +9,11 @@ import { HomeContent } from '@luna/screens/home/HomeContent';
99
import { getOrThrow } from '@luna/utils/result';
1010
import {
1111
Button,
12+
Chip,
13+
Dropdown,
14+
DropdownItem,
15+
DropdownMenu,
16+
DropdownTrigger,
1217
Spinner,
1318
Table,
1419
TableBody,
@@ -22,6 +27,7 @@ import { useAsyncList } from '@react-stately/data';
2227
import {
2328
IconEye,
2429
IconPencil,
30+
IconPlus,
2531
IconTrash,
2632
IconUserPlus,
2733
} from '@tabler/icons-react';
@@ -81,6 +87,20 @@ export function UsersView() {
8187
const [showUserDeleteModal, setShowUserDeleteModal] = useState(false);
8288
const [userId, setUserId] = useState(0);
8389

90+
const [allRoles, setAllRoles] = useState<Role[]>([]);
91+
92+
useEffect(() => {
93+
const fetchRoles = async () => {
94+
const rolesResult = await auth.getAllRoles();
95+
if (rolesResult.ok) {
96+
setAllRoles(rolesResult.value);
97+
} else {
98+
console.log('Could not get roles:', rolesResult.error);
99+
}
100+
};
101+
fetchRoles();
102+
}, [auth]);
103+
84104
return (
85105
// TODO: Lazy rendering
86106
<HomeContent
@@ -148,6 +168,9 @@ export function UsersView() {
148168
<TableColumn key="lastSeen" allowsSorting>
149169
Last Seen
150170
</TableColumn>
171+
<TableColumn key="roles" allowsSorting>
172+
Roles
173+
</TableColumn>
151174
<TableColumn key="actions">Actions</TableColumn>
152175
</TableHeader>
153176
<TableBody items={users.items} isLoading={isLoading}>
@@ -159,6 +182,69 @@ export function UsersView() {
159182
<TableCell>{user.createdAt.toLocaleString()}</TableCell>
160183
<TableCell>{user.updatedAt.toLocaleString()}</TableCell>
161184
<TableCell>{user.lastSeen.toLocaleString()}</TableCell>
185+
<TableCell>
186+
{user.roles.map(role => (
187+
<Chip
188+
key={role.id}
189+
onClose={async () => {
190+
console.log(
191+
'removing user',
192+
user.username,
193+
'from role',
194+
role.name
195+
);
196+
const result = await auth.removeUserFromRole(
197+
user.id,
198+
role.id
199+
);
200+
if (!result.ok) {
201+
console.log(result.error);
202+
return;
203+
}
204+
users.reload();
205+
}}
206+
className="m-1 hover:bg-danger"
207+
>
208+
{role.name}
209+
</Chip>
210+
))}
211+
<Dropdown>
212+
<DropdownTrigger>
213+
<Chip
214+
color="success"
215+
className="p-0 hover:cursor-pointer"
216+
variant="bordered"
217+
>
218+
<IconPlus size="15px" />
219+
</Chip>
220+
</DropdownTrigger>
221+
<DropdownMenu
222+
items={allRoles.filter(
223+
role =>
224+
undefined === user.roles.find(r => r.id === role.id)
225+
)}
226+
onAction={async key => {
227+
console.log('adding user', user.username, 'to role', key);
228+
const roleid: number = parseInt(key.toString());
229+
const result = await auth.addUserToRole(user.id, roleid);
230+
if (!result.ok) {
231+
console.log(result.error);
232+
return;
233+
}
234+
users.reload();
235+
}}
236+
>
237+
{(role: Role) => (
238+
<DropdownItem
239+
key={role.id}
240+
className="data-[hover=true]:bg-success"
241+
>
242+
{role.name}
243+
</DropdownItem>
244+
)}
245+
</DropdownMenu>
246+
</Dropdown>
247+
</TableCell>
162248
<TableCell>
163249
<div className="relative flex items-center gap-2">
164250
<Tooltip content="Details">

0 commit comments

Comments
 (0)