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

View File

@ -41,6 +41,8 @@ interface User {
id: string;
username: string;
email: string;
lastName: string;
firstName: string;
}
interface ApiError {
@ -249,22 +251,32 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
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);
if (user) {
setGroupMembers(prev => [...prev, user]);
// Update the members list
const updatedMember = availableUsers.find(u => u.id === userId);
if (updatedMember) {
setGroupMembers(prev => [...prev, updatedMember]);
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({
title: "Succès",
description: "Membre ajouté avec succès",
title: "Success",
description: "Member added successfully",
});
} catch (error) {
toast({
title: "Erreur",
description: "Erreur lors de l'ajout du membre",
title: "Error",
description: error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
}
@ -278,22 +290,32 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
method: "DELETE",
});
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));
if (!response.ok) {
throw new Error("Failed to remove member");
}
// 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({
title: "Succès",
description: "Membre retiré avec succès",
title: "Success",
description: "Member removed successfully",
});
} catch (error) {
toast({
title: "Erreur",
description: "Erreur lors du retrait du membre",
title: "Error",
description: error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
}
@ -439,16 +461,16 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
</DialogHeader>
<div className="space-y-4">
<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">
{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">
{groupMembers.map((member) => (
<div key={member.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md">
<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>
</div>
<Button
@ -465,26 +487,28 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
</div>
</div>
<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">
{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">
{availableUsers.map((user) => (
<div key={user.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md">
<div>
<p className="font-medium">{user.username}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
{availableUsers
.sort((a, b) => (a.lastName + a.firstName).localeCompare(b.lastName + b.firstName))
.map((user) => (
<div key={user.id} className="flex items-center justify-between p-2 hover:bg-muted rounded-md">
<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>
<Button
variant="ghost"
size="sm"
onClick={() => handleAddMember(user.id)}
>
<Plus className="h-4 w-4" />
</Button>
</div>
))}
</div>
)}

View File

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