update api users and groups and users 15

This commit is contained in:
Alma 2025-04-09 22:03:18 +02:00
parent d1780b748b
commit a7c9651690
3 changed files with 105 additions and 68 deletions

View File

@ -75,11 +75,11 @@ export async function GET() {
console.log("Filtered users count:", filteredUsers.length); console.log("Filtered users count:", filteredUsers.length);
// Fetch groups for each user // Fetch roles for each user
const usersWithGroups = await Promise.all(filteredUsers.map(async (user: any) => { const usersWithRoles = await Promise.all(filteredUsers.map(async (user: any) => {
try { try {
const groupsResponse = await fetch( const rolesResponse = await fetch(
`${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${user.id}/groups`, `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${user.id}/role-mappings/realm`,
{ {
headers: { headers: {
Authorization: `Bearer ${tokenData.access_token}`, Authorization: `Bearer ${tokenData.access_token}`,
@ -88,11 +88,16 @@ export async function GET() {
} }
); );
let groups = []; let roles = [];
if (groupsResponse.ok) { if (rolesResponse.ok) {
const groupsData = await groupsResponse.json(); const rolesData = await rolesResponse.json();
groups = groupsData.map((group: any) => group.name); roles = rolesData
console.log(`Groups for user ${user.username}:`, groups); .filter((role: any) =>
!role.name.startsWith('default-roles-') &&
!['offline_access', 'uma_authorization'].includes(role.name)
)
.map((role: any) => role.name);
console.log(`Roles for user ${user.username}:`, roles);
} }
return { return {
@ -102,10 +107,11 @@ export async function GET() {
lastName: user.lastName || '', lastName: user.lastName || '',
email: user.email, email: user.email,
createdTimestamp: user.createdTimestamp, createdTimestamp: user.createdTimestamp,
roles: groups, enabled: user.enabled,
roles: roles,
}; };
} catch (error) { } catch (error) {
console.error(`Error fetching groups for user ${user.id}:`, error); console.error(`Error fetching roles for user ${user.id}:`, error);
return { return {
id: user.id, id: user.id,
username: user.username, username: user.username,
@ -113,17 +119,18 @@ export async function GET() {
lastName: user.lastName || '', lastName: user.lastName || '',
email: user.email, email: user.email,
createdTimestamp: user.createdTimestamp, createdTimestamp: user.createdTimestamp,
enabled: user.enabled,
roles: [], roles: [],
}; };
} }
})); }));
console.log("Final users data:", usersWithGroups.map(u => ({ console.log("Final users data:", usersWithRoles.map(u => ({
username: u.username, username: u.username,
roles: u.roles, roles: u.roles,
}))); })));
return NextResponse.json(usersWithGroups); return NextResponse.json(usersWithRoles);
} catch (error) { } catch (error) {
console.error("Error:", error); console.error("Error:", error);

View File

@ -41,6 +41,8 @@ interface User {
id: string; id: string;
username: string; username: string;
email: string; email: string;
lastName: string;
firstName: string;
} }
interface ApiError { interface ApiError {
@ -249,22 +251,32 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
body: JSON.stringify({ userId }), body: JSON.stringify({ userId }),
}); });
if (!response.ok) throw new Error("Failed to add member"); if (!response.ok) {
throw new Error("Failed to add member");
}
const user = availableUsers.find(u => u.id === userId); // Update the members list
if (user) { const updatedMember = availableUsers.find(u => u.id === userId);
setGroupMembers(prev => [...prev, user]); if (updatedMember) {
setGroupMembers(prev => [...prev, updatedMember]);
setAvailableUsers(prev => prev.filter(u => u.id !== userId)); setAvailableUsers(prev => prev.filter(u => u.id !== userId));
} }
// Update the group's member count in the table
setGroups(prev => prev.map(group =>
group.id === selectedGroup.id
? { ...group, membersCount: group.membersCount + 1 }
: group
));
toast({ toast({
title: "Succès", title: "Success",
description: "Membre ajouté avec succès", description: "Member added successfully",
}); });
} catch (error) { } catch (error) {
toast({ toast({
title: "Erreur", title: "Error",
description: "Erreur lors de l'ajout du membre", description: error instanceof Error ? error.message : "An error occurred",
variant: "destructive", variant: "destructive",
}); });
} }
@ -278,22 +290,32 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
method: "DELETE", method: "DELETE",
}); });
if (!response.ok) throw new Error("Failed to remove member"); if (!response.ok) {
throw new Error("Failed to remove member");
const user = groupMembers.find(u => u.id === userId);
if (user) {
setAvailableUsers(prev => [...prev, user]);
setGroupMembers(prev => prev.filter(u => u.id !== userId));
} }
// Update the members list
const removedMember = groupMembers.find(u => u.id === userId);
if (removedMember) {
setGroupMembers(prev => prev.filter(u => u.id !== userId));
setAvailableUsers(prev => [...prev, removedMember]);
}
// Update the group's member count in the table
setGroups(prev => prev.map(group =>
group.id === selectedGroup.id
? { ...group, membersCount: Math.max(0, group.membersCount - 1) }
: group
));
toast({ toast({
title: "Succès", title: "Success",
description: "Membre retiré avec succès", description: "Member removed successfully",
}); });
} catch (error) { } catch (error) {
toast({ toast({
title: "Erreur", title: "Error",
description: "Erreur lors du retrait du membre", description: error instanceof Error ? error.message : "An error occurred",
variant: "destructive", variant: "destructive",
}); });
} }
@ -439,16 +461,16 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<Label>Membres actuels</Label> <Label>Current Members</Label>
<div className="max-h-[200px] overflow-y-auto border rounded-md p-2"> <div className="max-h-[200px] overflow-y-auto border rounded-md p-2">
{groupMembers.length === 0 ? ( {groupMembers.length === 0 ? (
<p className="text-sm text-muted-foreground">Aucun membre</p> <p className="text-sm text-muted-foreground">No members</p>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
{groupMembers.map((member) => ( {groupMembers.map((member) => (
<div key={member.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md"> <div key={member.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md">
<div> <div>
<p className="font-medium">{member.username}</p> <p className="font-medium">{member.lastName} {member.firstName}</p>
<p className="text-sm text-muted-foreground">{member.email}</p> <p className="text-sm text-muted-foreground">{member.email}</p>
</div> </div>
<Button <Button
@ -465,26 +487,28 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
</div> </div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label>Ajouter un membre</Label> <Label>Add Member</Label>
<div className="max-h-[200px] overflow-y-auto border rounded-md p-2"> <div className="max-h-[200px] overflow-y-auto border rounded-md p-2">
{availableUsers.length === 0 ? ( {availableUsers.length === 0 ? (
<p className="text-sm text-muted-foreground">Aucun utilisateur disponible</p> <p className="text-sm text-muted-foreground">No users available</p>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
{availableUsers.map((user) => ( {availableUsers
<div key={user.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md"> .sort((a, b) => (a.lastName + a.firstName).localeCompare(b.lastName + b.firstName))
<div> .map((user) => (
<p className="font-medium">{user.username}</p> <div key={user.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md">
<p className="text-sm text-muted-foreground">{user.email}</p> <div>
<p className="font-medium">{user.lastName} {user.firstName}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => handleAddMember(user.id)}
>
<Plus className="h-4 w-4" />
</Button>
</div> </div>
<Button
variant="ghost"
size="sm"
onClick={() => handleAddMember(user.id)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
))} ))}
</div> </div>
)} )}

View File

@ -507,12 +507,12 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead>Nom d'utilisateur</TableHead> <TableHead>Username</TableHead>
<TableHead>Prénom</TableHead> <TableHead>First Name</TableHead>
<TableHead>Nom</TableHead> <TableHead>Last Name</TableHead>
<TableHead>Email</TableHead> <TableHead>Email</TableHead>
<TableHead>Date d'inscription</TableHead> <TableHead>Created At</TableHead>
<TableHead>Rôles</TableHead> <TableHead>Roles</TableHead>
<TableHead className="text-right">Actions</TableHead> <TableHead className="text-right">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@ -526,7 +526,15 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
<TableCell> <TableCell>
{new Date(user.createdTimestamp).toLocaleDateString()} {new Date(user.createdTimestamp).toLocaleDateString()}
</TableCell> </TableCell>
<TableCell>{(user.roles || []).join(", ") || "-"}</TableCell> <TableCell>
<div className="flex flex-wrap gap-1">
{(user.roles || []).map((role) => (
<span key={role} className="inline-flex items-center rounded-full bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10">
{role}
</span>
))}
</div>
</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@ -695,34 +703,32 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
}}> }}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Gérer les rôles de {selectedUser?.username}</DialogTitle> <DialogTitle>Manage roles for {selectedUser?.username}</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<Label>les</Label> <Label>Available Roles</Label>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2 max-h-[200px] overflow-y-auto border rounded-md p-2">
{roles.map((role) => ( {roles.map((role) => (
<div key={role.id} className="flex items-center space-x-2 p-2 rounded-md hover:bg-gray-100"> <div key={role.id} className="flex items-center space-x-2 p-2 rounded-md hover:bg-gray-100">
<input <Checkbox
type="checkbox"
id={`manage-role-${role.id}`} id={`manage-role-${role.id}`}
checked={formData.roles.includes(role.name)} checked={formData.roles.includes(role.name)}
onChange={(e) => { onCheckedChange={(checked) => {
setFormData(prev => ({ setFormData(prev => ({
...prev, ...prev,
roles: e.target.checked roles: checked
? [...prev.roles, role.name] ? [...prev.roles, role.name]
: prev.roles.filter(r => r !== role.name) : prev.roles.filter(r => r !== role.name)
})); }));
}} }}
className="h-4 w-4 text-primary focus:ring-primary"
/> />
<label <Label
htmlFor={`manage-role-${role.id}`} htmlFor={`manage-role-${role.id}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" className="text-sm"
> >
{role.name} {role.name}
</label> </Label>
</div> </div>
))} ))}
</div> </div>
@ -736,10 +742,10 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
setFormData(prev => ({ ...prev, roles: [] })); setFormData(prev => ({ ...prev, roles: [] }));
}} }}
> >
Annuler Cancel
</Button> </Button>
<Button onClick={handleUpdateRoles}> <Button onClick={handleUpdateRoles}>
Mettre à jour les les Update Roles
</Button> </Button>
</div> </div>
</div> </div>