missions
This commit is contained in:
parent
27e59676b4
commit
c0f1f6c7de
@ -17,8 +17,14 @@ import {
|
|||||||
CardContent
|
CardContent
|
||||||
} from "../ui/card";
|
} from "../ui/card";
|
||||||
import { Badge } from "../ui/badge";
|
import { Badge } from "../ui/badge";
|
||||||
import { X, Search, UserPlus, Users } from "lucide-react";
|
import { X, Search, UserPlus, Users, PlusCircle, AlertCircle, Check } from "lucide-react";
|
||||||
import { toast } from "../ui/use-toast";
|
import { toast } from "../ui/use-toast";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger
|
||||||
|
} from "../ui/dropdown-menu";
|
||||||
|
|
||||||
// Define interfaces for user and group data
|
// Define interfaces for user and group data
|
||||||
interface User {
|
interface User {
|
||||||
@ -38,6 +44,10 @@ interface Group {
|
|||||||
membersCount: number;
|
membersCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User role types in mission
|
||||||
|
type GuardienRole = 'temps' | 'parole' | 'memoire';
|
||||||
|
type UserRole = GuardienRole | 'volontaire';
|
||||||
|
|
||||||
export function MissionsAdminPanel() {
|
export function MissionsAdminPanel() {
|
||||||
const [selectedServices, setSelectedServices] = useState<string[]>([]);
|
const [selectedServices, setSelectedServices] = useState<string[]>([]);
|
||||||
const [selectedProfils, setSelectedProfils] = useState<string[]>([]);
|
const [selectedProfils, setSelectedProfils] = useState<string[]>([]);
|
||||||
@ -46,12 +56,16 @@ export function MissionsAdminPanel() {
|
|||||||
const [gardienDuTemps, setGardienDuTemps] = useState<string | null>(null);
|
const [gardienDuTemps, setGardienDuTemps] = useState<string | null>(null);
|
||||||
const [gardienDeLaParole, setGardienDeLaParole] = useState<string | null>(null);
|
const [gardienDeLaParole, setGardienDeLaParole] = useState<string | null>(null);
|
||||||
const [gardienDeLaMemoire, setGardienDeLaMemoire] = useState<string | null>(null);
|
const [gardienDeLaMemoire, setGardienDeLaMemoire] = useState<string | null>(null);
|
||||||
|
const [volontaires, setVolontaires] = useState<string[]>([]);
|
||||||
|
|
||||||
// State for storing fetched data
|
// State for storing fetched data
|
||||||
const [users, setUsers] = useState<User[]>([]);
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
const [groups, setGroups] = useState<Group[]>([]);
|
const [groups, setGroups] = useState<Group[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// Check if mission is valid (has all required guardiens)
|
||||||
|
const isMissionValid = gardienDuTemps !== null && gardienDeLaParole !== null && gardienDeLaMemoire !== null;
|
||||||
|
|
||||||
// Fetch users and groups on component mount
|
// Fetch users and groups on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@ -140,23 +154,40 @@ export function MissionsAdminPanel() {
|
|||||||
(group.name?.toLowerCase() || "").includes(searchTerm.toLowerCase())
|
(group.name?.toLowerCase() || "").includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
// Function to check if a user is already assigned for a role
|
// Function to check if a user is already assigned to any role
|
||||||
const isUserAssigned = (userId: string) => {
|
const isUserAssigned = (userId: string) => {
|
||||||
return gardienDuTemps === userId ||
|
return gardienDuTemps === userId ||
|
||||||
gardienDeLaParole === userId ||
|
gardienDeLaParole === userId ||
|
||||||
gardienDeLaMemoire === userId;
|
gardienDeLaMemoire === userId ||
|
||||||
|
volontaires.includes(userId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to get role name by user id
|
// Function to get user's role
|
||||||
const getUserRole = (userId: string) => {
|
const getUserRole = (userId: string): UserRole | null => {
|
||||||
if (gardienDuTemps === userId) return "Gardien du Temps";
|
if (gardienDuTemps === userId) return 'temps';
|
||||||
if (gardienDeLaParole === userId) return "Gardien de la Parole";
|
if (gardienDeLaParole === userId) return 'parole';
|
||||||
if (gardienDeLaMemoire === userId) return "Gardien de la Mémoire";
|
if (gardienDeLaMemoire === userId) return 'memoire';
|
||||||
|
if (volontaires.includes(userId)) return 'volontaire';
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to assign a user to a role
|
// Function to get role display name
|
||||||
const assignUserRole = (userId: string, role: 'temps' | 'parole' | 'memoire') => {
|
const getRoleDisplayName = (role: UserRole | null): string => {
|
||||||
|
switch(role) {
|
||||||
|
case 'temps': return "Gardien du Temps";
|
||||||
|
case 'parole': return "Gardien de la Parole";
|
||||||
|
case 'memoire': return "Gardien de la Mémoire";
|
||||||
|
case 'volontaire': return "Volontaire";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to assign a user to a specific guardian role
|
||||||
|
const assignGuardienRole = (userId: string, role: GuardienRole) => {
|
||||||
|
// Remove from any existing role first
|
||||||
|
removeUserFromAllRoles(userId);
|
||||||
|
|
||||||
|
// Assign to new role
|
||||||
if (role === 'temps') {
|
if (role === 'temps') {
|
||||||
setGardienDuTemps(userId);
|
setGardienDuTemps(userId);
|
||||||
} else if (role === 'parole') {
|
} else if (role === 'parole') {
|
||||||
@ -164,19 +195,42 @@ export function MissionsAdminPanel() {
|
|||||||
} else if (role === 'memoire') {
|
} else if (role === 'memoire') {
|
||||||
setGardienDeLaMemoire(userId);
|
setGardienDeLaMemoire(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Rôle assigné",
|
||||||
|
description: `L'utilisateur a été assigné comme ${getRoleDisplayName(role)}`,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to remove a user from a role
|
// Function to assign a user as volunteer
|
||||||
const removeUserRole = (role: 'temps' | 'parole' | 'memoire') => {
|
const assignVolontaire = (userId: string) => {
|
||||||
if (role === 'temps') {
|
// Remove from any existing role first
|
||||||
setGardienDuTemps(null);
|
removeUserFromAllRoles(userId);
|
||||||
} else if (role === 'parole') {
|
|
||||||
setGardienDeLaParole(null);
|
// Add to volunteers
|
||||||
} else if (role === 'memoire') {
|
setVolontaires(prev => [...prev, userId]);
|
||||||
setGardienDeLaMemoire(null);
|
|
||||||
|
toast({
|
||||||
|
title: "Rôle assigné",
|
||||||
|
description: "L'utilisateur a été assigné comme Volontaire",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to remove a user from all roles
|
||||||
|
const removeUserFromAllRoles = (userId: string) => {
|
||||||
|
if (gardienDuTemps === userId) setGardienDuTemps(null);
|
||||||
|
if (gardienDeLaParole === userId) setGardienDeLaParole(null);
|
||||||
|
if (gardienDeLaMemoire === userId) setGardienDeLaMemoire(null);
|
||||||
|
if (volontaires.includes(userId)) {
|
||||||
|
setVolontaires(prev => prev.filter(id => id !== userId));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if all guardian roles are filled
|
||||||
|
const areAllGuardiensFilled = (): boolean => {
|
||||||
|
return gardienDuTemps !== null && gardienDeLaParole !== null && gardienDeLaMemoire !== null;
|
||||||
|
};
|
||||||
|
|
||||||
// Function to fetch group members
|
// Function to fetch group members
|
||||||
const fetchGroupMembers = async (groupId: string) => {
|
const fetchGroupMembers = async (groupId: string) => {
|
||||||
try {
|
try {
|
||||||
@ -632,7 +686,24 @@ export function MissionsAdminPanel() {
|
|||||||
<TabsContent value="membres" className="space-y-6">
|
<TabsContent value="membres" className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-medium text-gray-700 mb-4">Les Gardiens de l'Intention</h3>
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h3 className="text-lg font-medium text-gray-700">Les Gardiens de l'Intention</h3>
|
||||||
|
|
||||||
|
<div className="flex items-center">
|
||||||
|
{!isMissionValid && (
|
||||||
|
<div className="flex items-center text-amber-600 mr-3 text-sm">
|
||||||
|
<AlertCircle size={16} className="mr-1" />
|
||||||
|
Les 3 gardiens doivent être assignés
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isMissionValid && (
|
||||||
|
<div className="flex items-center text-green-600 mr-3 text-sm">
|
||||||
|
<Check size={16} className="mr-1" />
|
||||||
|
Tous les gardiens sont assignés
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Gardien du Temps */}
|
{/* Gardien du Temps */}
|
||||||
@ -643,7 +714,7 @@ export function MissionsAdminPanel() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => removeUserRole('temps')}
|
onClick={() => removeUserFromAllRoles(gardienDuTemps)}
|
||||||
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-8"
|
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-8"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
@ -699,7 +770,7 @@ export function MissionsAdminPanel() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => removeUserRole('parole')}
|
onClick={() => removeUserFromAllRoles(gardienDeLaParole)}
|
||||||
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-8"
|
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-8"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
@ -755,7 +826,7 @@ export function MissionsAdminPanel() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => removeUserRole('memoire')}
|
onClick={() => removeUserFromAllRoles(gardienDeLaMemoire)}
|
||||||
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-8"
|
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-8"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
@ -837,6 +908,33 @@ export function MissionsAdminPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-4">
|
||||||
|
<h5 className="text-sm font-medium text-gray-700 mb-2">Volontaires ({volontaires.length})</h5>
|
||||||
|
{volontaires.length > 0 ? (
|
||||||
|
<div className="flex flex-wrap gap-2 mb-3">
|
||||||
|
{volontaires.map(userId => {
|
||||||
|
const user = users.find(u => u.id === userId);
|
||||||
|
if (!user) return null;
|
||||||
|
return (
|
||||||
|
<Badge key={userId} className="bg-gray-100 text-gray-800 hover:bg-gray-200 px-2 py-1 flex items-center">
|
||||||
|
{user.firstName} {user.lastName}
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => removeUserFromAllRoles(userId)}
|
||||||
|
className="ml-1 h-5 w-5 p-0 text-gray-500 hover:text-red-600 hover:bg-transparent"
|
||||||
|
>
|
||||||
|
<X size={12} />
|
||||||
|
</Button>
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-sm text-gray-500 mb-3">Aucun volontaire assigné</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="relative mb-4">
|
<div className="relative mb-4">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500" />
|
||||||
<Input
|
<Input
|
||||||
@ -880,19 +978,23 @@ export function MissionsAdminPanel() {
|
|||||||
<div className="text-sm text-gray-500">{user.email}</div>
|
<div className="text-sm text-gray-500">{user.email}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* User role controls */}
|
||||||
{isUserAssigned(user.id) ? (
|
{isUserAssigned(user.id) ? (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Badge className="bg-blue-100 text-blue-800 hover:bg-blue-200 px-2 py-1 mr-2">
|
<Badge
|
||||||
{getUserRole(user.id)}
|
className={`px-2 py-1 mr-2 ${
|
||||||
|
getUserRole(user.id) === 'volontaire'
|
||||||
|
? 'bg-gray-100 text-gray-800 hover:bg-gray-200'
|
||||||
|
: 'bg-blue-100 text-blue-800 hover:bg-blue-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{getRoleDisplayName(getUserRole(user.id))}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => removeUserFromAllRoles(user.id)}
|
||||||
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 hover:text-red-700 border-red-200 h-7 px-2"
|
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200 h-7 px-2"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
@ -900,20 +1002,53 @@ export function MissionsAdminPanel() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex space-x-2">
|
<div className="flex">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="text-blue-600 bg-white hover:bg-blue-50 hover:text-blue-700 border-blue-200 h-8 mr-2"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
<UserPlus size={16} className="mr-1" />
|
||||||
|
Gardien
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="bg-white border border-gray-200">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => assignGuardienRole(user.id, 'temps')}
|
||||||
|
disabled={gardienDuTemps !== null}
|
||||||
|
className={`cursor-pointer ${gardienDuTemps !== null ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||||
|
>
|
||||||
|
Gardien du Temps
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => assignGuardienRole(user.id, 'parole')}
|
||||||
|
disabled={gardienDeLaParole !== null}
|
||||||
|
className={`cursor-pointer ${gardienDeLaParole !== null ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||||
|
>
|
||||||
|
Gardien de la Parole
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => assignGuardienRole(user.id, 'memoire')}
|
||||||
|
disabled={gardienDeLaMemoire !== null}
|
||||||
|
className={`cursor-pointer ${gardienDeLaMemoire !== null ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||||
|
>
|
||||||
|
Gardien de la Mémoire
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => assignVolontaire(user.id)}
|
||||||
if (!gardienDuTemps) assignUserRole(user.id, 'temps');
|
className="text-gray-700 bg-white hover:bg-gray-50 border-gray-300 h-8"
|
||||||
else if (!gardienDeLaParole) assignUserRole(user.id, 'parole');
|
disabled={loading}
|
||||||
else if (!gardienDeLaMemoire) assignUserRole(user.id, 'memoire');
|
|
||||||
}}
|
|
||||||
disabled={(gardienDuTemps !== null && gardienDeLaParole !== null && gardienDeLaMemoire !== null) || loading}
|
|
||||||
className="text-blue-600 bg-white hover:bg-blue-50 hover:text-blue-700 border-blue-200 h-8"
|
|
||||||
>
|
>
|
||||||
<UserPlus size={16} className="mr-1" />
|
<PlusCircle size={16} className="mr-1" />
|
||||||
Assigner
|
Volontaire
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -967,8 +1102,27 @@ export function MissionsAdminPanel() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<div className="mt-8 flex justify-end">
|
<div className="mt-8 flex justify-end">
|
||||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white">
|
<Button
|
||||||
Save & Preview
|
className={`${isMissionValid
|
||||||
|
? 'bg-blue-600 hover:bg-blue-700'
|
||||||
|
: 'bg-gray-400 hover:bg-gray-500 cursor-not-allowed'} text-white`}
|
||||||
|
disabled={!isMissionValid}
|
||||||
|
onClick={() => {
|
||||||
|
if (isMissionValid) {
|
||||||
|
toast({
|
||||||
|
title: "Mission enregistrée",
|
||||||
|
description: "Tous les gardiens ont été assignés avec succès.",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
title: "Mission incomplète",
|
||||||
|
description: "Vous devez assigner les trois gardiens avant de pouvoir enregistrer.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isMissionValid ? "Enregistrer" : "Gardiens manquants"}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="ml-2">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="ml-2">
|
||||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
<polyline points="12 5 19 12 12 19"></polyline>
|
<polyline points="12 5 19 12 12 19"></polyline>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user