missions
This commit is contained in:
parent
cefc9fd072
commit
a1cb75a1ec
@ -18,22 +18,25 @@ import {
|
||||
} from "../ui/card";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { X, Search, UserPlus, Users } from "lucide-react";
|
||||
import { toast } from "../ui/use-toast";
|
||||
|
||||
// Mock user data - in a real app, this would come from an API
|
||||
const mockUsers = [
|
||||
{ id: '1', username: 'user1', firstName: 'John', lastName: 'Doe', email: 'john@example.com' },
|
||||
{ id: '2', username: 'user2', firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com' },
|
||||
{ id: '3', username: 'user3', firstName: 'Alice', lastName: 'Johnson', email: 'alice@example.com' },
|
||||
{ id: '4', username: 'user4', firstName: 'Bob', lastName: 'Brown', email: 'bob@example.com' },
|
||||
{ id: '5', username: 'user5', firstName: 'Eva', lastName: 'Martinez', email: 'eva@example.com' },
|
||||
];
|
||||
// Define interfaces for user and group data
|
||||
interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
roles?: string[];
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
// Mock group data
|
||||
const mockGroups = [
|
||||
{ id: '1', name: 'Group A', path: '/group-a', membersCount: 3 },
|
||||
{ id: '2', name: 'Group B', path: '/group-b', membersCount: 5 },
|
||||
{ id: '3', name: 'Group C', path: '/group-c', membersCount: 2 },
|
||||
];
|
||||
interface Group {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
membersCount: number;
|
||||
}
|
||||
|
||||
export function MissionsAdminPanel() {
|
||||
const [selectedServices, setSelectedServices] = useState<string[]>([]);
|
||||
@ -43,21 +46,101 @@ export function MissionsAdminPanel() {
|
||||
const [gardienDuTemps, setGardienDuTemps] = useState<string | null>(null);
|
||||
const [gardienDeLaParole, setGardienDeLaParole] = useState<string | null>(null);
|
||||
const [gardienDeLaMemoire, setGardienDeLaMemoire] = useState<string | null>(null);
|
||||
|
||||
|
||||
// State for storing fetched data
|
||||
const [users, setUsers] = useState<User[]>([]);
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Fetch users and groups on component mount
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await Promise.all([fetchUsers(), fetchGroups()]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// Function to fetch users from API
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/users");
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch users");
|
||||
}
|
||||
const data = await response.json();
|
||||
setUsers(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching users:", error);
|
||||
toast({
|
||||
title: "Erreur",
|
||||
description: "Erreur lors de la récupération des utilisateurs",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Function to fetch groups from API
|
||||
const fetchGroups = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/groups");
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch groups");
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
// Fetch member counts for groups
|
||||
const groupsWithCounts = await Promise.all(
|
||||
(Array.isArray(data) ? data : []).map(async (group) => {
|
||||
try {
|
||||
const membersResponse = await fetch(`/api/groups/${group.id}/members`);
|
||||
if (membersResponse.ok) {
|
||||
const members = await membersResponse.json();
|
||||
return {
|
||||
...group,
|
||||
membersCount: Array.isArray(members) ? members.length : 0
|
||||
};
|
||||
}
|
||||
return {...group, membersCount: 0};
|
||||
} catch (error) {
|
||||
console.error(`Error fetching members for group ${group.id}:`, error);
|
||||
return {...group, membersCount: 0};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setGroups(groupsWithCounts);
|
||||
} catch (error) {
|
||||
console.error("Error fetching groups:", error);
|
||||
toast({
|
||||
title: "Erreur",
|
||||
description: "Erreur lors de la récupération des groupes",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Filtered users based on search term
|
||||
const filteredUsers = mockUsers.filter(user =>
|
||||
user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
user.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
user.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
user.lastName.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
const filteredUsers = users.filter(user =>
|
||||
(user.username?.toLowerCase() || "").includes(searchTerm.toLowerCase()) ||
|
||||
(user.email?.toLowerCase() || "").includes(searchTerm.toLowerCase()) ||
|
||||
(user.firstName?.toLowerCase() || "").includes(searchTerm.toLowerCase()) ||
|
||||
(user.lastName?.toLowerCase() || "").includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
// Filtered groups based on search term
|
||||
const filteredGroups = mockGroups.filter(group =>
|
||||
group.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
const filteredGroups = groups.filter(group =>
|
||||
(group.name?.toLowerCase() || "").includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
// Function to check if a user is already selected for a role
|
||||
// Function to check if a user is already assigned for a role
|
||||
const isUserAssigned = (userId: string) => {
|
||||
return gardienDuTemps === userId ||
|
||||
gardienDeLaParole === userId ||
|
||||
@ -94,6 +177,44 @@ export function MissionsAdminPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
// Function to fetch group members
|
||||
const fetchGroupMembers = async (groupId: string) => {
|
||||
try {
|
||||
const response = await fetch(`/api/groups/${groupId}/members`);
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch group members");
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching members for group ${groupId}:`, error);
|
||||
toast({
|
||||
title: "Erreur",
|
||||
description: "Erreur lors de la récupération des membres du groupe",
|
||||
variant: "destructive",
|
||||
});
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// Handler for viewing group members
|
||||
const handleViewGroupMembers = async (groupId: string, groupName: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const members = await fetchGroupMembers(groupId);
|
||||
// Here you would typically open a dialog to show members
|
||||
// For this implementation, we'll just show a toast with the count
|
||||
toast({
|
||||
title: `Membres de ${groupName}`,
|
||||
description: `${members.length} membres trouvés dans ce groupe`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error handling group members:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Card className="border shadow-sm bg-white">
|
||||
@ -524,36 +645,49 @@ export function MissionsAdminPanel() {
|
||||
size="sm"
|
||||
onClick={() => removeUserRole('temps')}
|
||||
className="text-red-600 hover:bg-red-50 border-red-200 h-8"
|
||||
disabled={loading}
|
||||
>
|
||||
<X size={16} className="mr-1" />
|
||||
Supprimer
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{gardienDuTemps ? (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-md p-3">
|
||||
{(() => {
|
||||
const user = mockUsers.find(u => u.id === gardienDuTemps);
|
||||
return user ? (
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-medium mr-3">
|
||||
{user.firstName[0]}{user.lastName[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : "Utilisateur non trouvé";
|
||||
})()}
|
||||
{loading ? (
|
||||
<div className="flex items-center py-2 px-3 bg-gray-50 border border-gray-200 rounded-md">
|
||||
<div className="animate-pulse w-full flex items-center">
|
||||
<div className="h-10 w-10 bg-gray-300 rounded-full mr-3"></div>
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="h-3 bg-gray-300 rounded w-1/3"></div>
|
||||
<div className="h-3 bg-gray-300 rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-500 bg-gray-50 border border-gray-200 rounded-md py-2 px-3">
|
||||
<div className="h-8 w-8 rounded-full bg-gray-200 flex items-center justify-center mr-3">
|
||||
<Users size={14} className="text-gray-400" />
|
||||
gardienDuTemps ? (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-md p-3">
|
||||
{(() => {
|
||||
const user = users.find(u => u.id === gardienDuTemps);
|
||||
return user ? (
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-medium mr-3">
|
||||
{user.firstName?.[0] || ""}{user.lastName?.[0] || ""}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : "Utilisateur non trouvé";
|
||||
})()}
|
||||
</div>
|
||||
<span className="text-sm">Aucun utilisateur sélectionné</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-500 bg-gray-50 border border-gray-200 rounded-md py-2 px-3">
|
||||
<div className="h-8 w-8 rounded-full bg-gray-200 flex items-center justify-center mr-3">
|
||||
<Users size={14} className="text-gray-400" />
|
||||
</div>
|
||||
<span className="text-sm">Aucun utilisateur sélectionné</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -567,36 +701,49 @@ export function MissionsAdminPanel() {
|
||||
size="sm"
|
||||
onClick={() => removeUserRole('parole')}
|
||||
className="text-red-600 hover:bg-red-50 border-red-200 h-8"
|
||||
disabled={loading}
|
||||
>
|
||||
<X size={16} className="mr-1" />
|
||||
Supprimer
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{gardienDeLaParole ? (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-md p-3">
|
||||
{(() => {
|
||||
const user = mockUsers.find(u => u.id === gardienDeLaParole);
|
||||
return user ? (
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-medium mr-3">
|
||||
{user.firstName[0]}{user.lastName[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : "Utilisateur non trouvé";
|
||||
})()}
|
||||
{loading ? (
|
||||
<div className="flex items-center py-2 px-3 bg-gray-50 border border-gray-200 rounded-md">
|
||||
<div className="animate-pulse w-full flex items-center">
|
||||
<div className="h-10 w-10 bg-gray-300 rounded-full mr-3"></div>
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="h-3 bg-gray-300 rounded w-1/3"></div>
|
||||
<div className="h-3 bg-gray-300 rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-500 bg-gray-50 border border-gray-200 rounded-md py-2 px-3">
|
||||
<div className="h-8 w-8 rounded-full bg-gray-200 flex items-center justify-center mr-3">
|
||||
<Users size={14} className="text-gray-400" />
|
||||
gardienDeLaParole ? (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-md p-3">
|
||||
{(() => {
|
||||
const user = users.find(u => u.id === gardienDeLaParole);
|
||||
return user ? (
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-medium mr-3">
|
||||
{user.firstName?.[0] || ""}{user.lastName?.[0] || ""}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : "Utilisateur non trouvé";
|
||||
})()}
|
||||
</div>
|
||||
<span className="text-sm">Aucun utilisateur sélectionné</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-500 bg-gray-50 border border-gray-200 rounded-md py-2 px-3">
|
||||
<div className="h-8 w-8 rounded-full bg-gray-200 flex items-center justify-center mr-3">
|
||||
<Users size={14} className="text-gray-400" />
|
||||
</div>
|
||||
<span className="text-sm">Aucun utilisateur sélectionné</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -610,36 +757,49 @@ export function MissionsAdminPanel() {
|
||||
size="sm"
|
||||
onClick={() => removeUserRole('memoire')}
|
||||
className="text-red-600 hover:bg-red-50 border-red-200 h-8"
|
||||
disabled={loading}
|
||||
>
|
||||
<X size={16} className="mr-1" />
|
||||
Supprimer
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{gardienDeLaMemoire ? (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-md p-3">
|
||||
{(() => {
|
||||
const user = mockUsers.find(u => u.id === gardienDeLaMemoire);
|
||||
return user ? (
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-medium mr-3">
|
||||
{user.firstName[0]}{user.lastName[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : "Utilisateur non trouvé";
|
||||
})()}
|
||||
{loading ? (
|
||||
<div className="flex items-center py-2 px-3 bg-gray-50 border border-gray-200 rounded-md">
|
||||
<div className="animate-pulse w-full flex items-center">
|
||||
<div className="h-10 w-10 bg-gray-300 rounded-full mr-3"></div>
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="h-3 bg-gray-300 rounded w-1/3"></div>
|
||||
<div className="h-3 bg-gray-300 rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-500 bg-gray-50 border border-gray-200 rounded-md py-2 px-3">
|
||||
<div className="h-8 w-8 rounded-full bg-gray-200 flex items-center justify-center mr-3">
|
||||
<Users size={14} className="text-gray-400" />
|
||||
gardienDeLaMemoire ? (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-md p-3">
|
||||
{(() => {
|
||||
const user = users.find(u => u.id === gardienDeLaMemoire);
|
||||
return user ? (
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 font-medium mr-3">
|
||||
{user.firstName?.[0] || ""}{user.lastName?.[0] || ""}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : "Utilisateur non trouvé";
|
||||
})()}
|
||||
</div>
|
||||
<span className="text-sm">Aucun utilisateur sélectionné</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-500 bg-gray-50 border border-gray-200 rounded-md py-2 px-3">
|
||||
<div className="h-8 w-8 rounded-full bg-gray-200 flex items-center justify-center mr-3">
|
||||
<Users size={14} className="text-gray-400" />
|
||||
</div>
|
||||
<span className="text-sm">Aucun utilisateur sélectionné</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -655,6 +815,7 @@ export function MissionsAdminPanel() {
|
||||
size="sm"
|
||||
onClick={() => setSelectedTab('users')}
|
||||
className={selectedTab === 'users' ? 'bg-blue-600 text-white' : 'text-gray-700'}
|
||||
disabled={loading}
|
||||
>
|
||||
<Users size={16} className="mr-1" />
|
||||
Utilisateurs
|
||||
@ -664,6 +825,7 @@ export function MissionsAdminPanel() {
|
||||
size="sm"
|
||||
onClick={() => setSelectedTab('groups')}
|
||||
className={selectedTab === 'groups' ? 'bg-blue-600 text-white' : 'text-gray-700'}
|
||||
disabled={loading}
|
||||
>
|
||||
<Users size={16} className="mr-1" />
|
||||
Groupes
|
||||
@ -679,100 +841,121 @@ export function MissionsAdminPanel() {
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-9 bg-white text-gray-900 border-gray-300"
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="border rounded-md max-h-[300px] overflow-y-auto">
|
||||
{selectedTab === 'users' ? (
|
||||
filteredUsers.length > 0 ? (
|
||||
<div className="divide-y divide-gray-200">
|
||||
{filteredUsers.map(user => (
|
||||
<div key={user.id} className="p-3 hover:bg-gray-50 flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 font-medium mr-3">
|
||||
{user.firstName[0]}{user.lastName[0]}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
{isUserAssigned(user.id) ? (
|
||||
{loading ? (
|
||||
<div className="border rounded-md p-6 flex flex-col items-center justify-center text-gray-500">
|
||||
<div className="animate-pulse space-y-4 w-full">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="flex items-center py-3 px-4">
|
||||
<div className="h-10 w-10 bg-gray-300 rounded-full mr-3"></div>
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="h-3 bg-gray-300 rounded w-1/3"></div>
|
||||
<div className="h-3 bg-gray-300 rounded w-1/2"></div>
|
||||
</div>
|
||||
<div className="h-7 w-16 bg-gray-300 rounded"></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="border rounded-md max-h-[300px] overflow-y-auto">
|
||||
{selectedTab === 'users' ? (
|
||||
filteredUsers.length > 0 ? (
|
||||
<div className="divide-y divide-gray-200">
|
||||
{filteredUsers.map(user => (
|
||||
<div key={user.id} className="p-3 hover:bg-gray-50 flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<Badge className="bg-blue-100 text-blue-800 hover:bg-blue-200 px-2 py-1 mr-2">
|
||||
{getUserRole(user.id)}
|
||||
</Badge>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (gardienDuTemps === user.id) removeUserRole('temps');
|
||||
if (gardienDeLaParole === user.id) removeUserRole('parole');
|
||||
if (gardienDeLaMemoire === user.id) removeUserRole('memoire');
|
||||
}}
|
||||
className="text-red-600 hover:bg-red-50 border-red-200 h-7 px-2"
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex space-x-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (!gardienDuTemps) assignUserRole(user.id, 'temps');
|
||||
else if (!gardienDeLaParole) assignUserRole(user.id, 'parole');
|
||||
else if (!gardienDeLaMemoire) assignUserRole(user.id, 'memoire');
|
||||
}}
|
||||
disabled={gardienDuTemps !== null && gardienDeLaParole !== null && gardienDeLaMemoire !== null}
|
||||
className="text-blue-600 hover:bg-blue-50 border-blue-200 h-8"
|
||||
>
|
||||
<UserPlus size={16} className="mr-1" />
|
||||
Assigner
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-4 text-center text-gray-500">
|
||||
Aucun utilisateur trouvé
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
filteredGroups.length > 0 ? (
|
||||
<div className="divide-y divide-gray-200">
|
||||
{filteredGroups.map(group => (
|
||||
<div key={group.id} className="p-3 hover:bg-gray-50 flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 font-medium mr-3">
|
||||
<Users size={16} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{group.name}</div>
|
||||
<div className="text-sm text-gray-500">{group.membersCount} membres</div>
|
||||
<div className="h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 font-medium mr-3">
|
||||
{user.firstName?.[0] || ""}{user.lastName?.[0] || ""}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{user.firstName} {user.lastName}</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
{isUserAssigned(user.id) ? (
|
||||
<div className="flex items-center">
|
||||
<Badge className="bg-blue-100 text-blue-800 hover:bg-blue-200 px-2 py-1 mr-2">
|
||||
{getUserRole(user.id)}
|
||||
</Badge>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (gardienDuTemps === user.id) removeUserRole('temps');
|
||||
if (gardienDeLaParole === user.id) removeUserRole('parole');
|
||||
if (gardienDeLaMemoire === user.id) removeUserRole('memoire');
|
||||
}}
|
||||
className="text-red-600 hover:bg-red-50 border-red-200 h-7 px-2"
|
||||
disabled={loading}
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex space-x-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (!gardienDuTemps) assignUserRole(user.id, 'temps');
|
||||
else if (!gardienDeLaParole) assignUserRole(user.id, 'parole');
|
||||
else if (!gardienDeLaMemoire) assignUserRole(user.id, 'memoire');
|
||||
}}
|
||||
disabled={(gardienDuTemps !== null && gardienDeLaParole !== null && gardienDeLaMemoire !== null) || loading}
|
||||
className="text-blue-600 hover:bg-blue-50 border-blue-200 h-8"
|
||||
>
|
||||
<UserPlus size={16} className="mr-1" />
|
||||
Assigner
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-blue-600 hover:bg-blue-50 border-blue-200 h-8"
|
||||
>
|
||||
<Users size={16} className="mr-1" />
|
||||
Voir membres
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-4 text-center text-gray-500">
|
||||
Aucun utilisateur trouvé
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="p-4 text-center text-gray-500">
|
||||
Aucun groupe trouvé
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
filteredGroups.length > 0 ? (
|
||||
<div className="divide-y divide-gray-200">
|
||||
{filteredGroups.map(group => (
|
||||
<div key={group.id} className="p-3 hover:bg-gray-50 flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 font-medium mr-3">
|
||||
<Users size={16} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{group.name}</div>
|
||||
<div className="text-sm text-gray-500">{group.membersCount} membres</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewGroupMembers(group.id, group.name)}
|
||||
className="text-blue-600 hover:bg-blue-50 border-blue-200 h-8"
|
||||
disabled={loading}
|
||||
>
|
||||
<Users size={16} className="mr-1" />
|
||||
Voir membres
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-4 text-center text-gray-500">
|
||||
Aucun groupe trouvé
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user