Checkpoint: Restreindre les droits des formateurs : masquer les boutons de création/modification/suppression des formations et séquences pour les formateurs (seuls les administrateurs peuvent les voir)
This commit is contained in:
@@ -13,8 +13,11 @@ import { Plus, Pencil, Trash2, ExternalLink, Search, SlidersHorizontal } from "l
|
|||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { useAuth } from "@/_core/hooks/useAuth";
|
||||||
|
|
||||||
export default function AdminFormations() {
|
export default function AdminFormations() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const isAdmin = user?.role === 'admin';
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [editingId, setEditingId] = useState<number | null>(null);
|
const [editingId, setEditingId] = useState<number | null>(null);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
@@ -130,12 +133,14 @@ export default function AdminFormations() {
|
|||||||
setOpen(isOpen);
|
setOpen(isOpen);
|
||||||
if (!isOpen) resetForm();
|
if (!isOpen) resetForm();
|
||||||
}}>
|
}}>
|
||||||
<DialogTrigger asChild>
|
{isAdmin && (
|
||||||
<Button>
|
<DialogTrigger asChild>
|
||||||
<Plus className="w-4 h-4 mr-2" />
|
<Button>
|
||||||
Nouvelle formation
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
</Button>
|
Nouvelle formation
|
||||||
</DialogTrigger>
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
)}
|
||||||
<DialogContent className="sm:max-w-[500px]">
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
@@ -301,22 +306,24 @@ export default function AdminFormations() {
|
|||||||
</span>
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
<div className="flex justify-end gap-2">
|
{isAdmin && (
|
||||||
<Button
|
<div className="flex justify-end gap-2">
|
||||||
variant="ghost"
|
<Button
|
||||||
size="icon"
|
variant="ghost"
|
||||||
onClick={() => handleEdit(formation)}
|
size="icon"
|
||||||
>
|
onClick={() => handleEdit(formation)}
|
||||||
<Pencil className="w-4 h-4" />
|
>
|
||||||
</Button>
|
<Pencil className="w-4 h-4" />
|
||||||
<Button
|
</Button>
|
||||||
variant="ghost"
|
<Button
|
||||||
size="icon"
|
variant="ghost"
|
||||||
onClick={() => handleDelete(formation.id)}
|
size="icon"
|
||||||
>
|
onClick={() => handleDelete(formation.id)}
|
||||||
<Trash2 className="w-4 h-4 text-red-600" />
|
>
|
||||||
</Button>
|
<Trash2 className="w-4 h-4 text-red-600" />
|
||||||
</div>
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import CapacityProgressBar from "@/components/CapacityProgressBar";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useLocation } from "wouter";
|
import { useLocation } from "wouter";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import { useAuth } from "@/_core/hooks/useAuth";
|
||||||
|
|
||||||
interface DateFormation {
|
interface DateFormation {
|
||||||
dateDebut: string;
|
dateDebut: string;
|
||||||
@@ -23,6 +24,8 @@ interface DateFormation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AdminSequences() {
|
export default function AdminSequences() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const isAdmin = user?.role === 'admin';
|
||||||
const [, setLocation] = useLocation();
|
const [, setLocation] = useLocation();
|
||||||
const [isCreateOpen, setIsCreateOpen] = useState(false);
|
const [isCreateOpen, setIsCreateOpen] = useState(false);
|
||||||
const [isEditOpen, setIsEditOpen] = useState(false);
|
const [isEditOpen, setIsEditOpen] = useState(false);
|
||||||
@@ -457,12 +460,14 @@ export default function AdminSequences() {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Dialog open={isCreateOpen} onOpenChange={setIsCreateOpen}>
|
<Dialog open={isCreateOpen} onOpenChange={setIsCreateOpen}>
|
||||||
<DialogTrigger asChild>
|
{isAdmin && (
|
||||||
<Button onClick={resetForm}>
|
<DialogTrigger asChild>
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
<Button onClick={resetForm}>
|
||||||
Nouvelle Séquence
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
</Button>
|
Nouvelle Séquence
|
||||||
</DialogTrigger>
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
)}
|
||||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Créer une nouvelle séquence</DialogTitle>
|
<DialogTitle>Créer une nouvelle séquence</DialogTitle>
|
||||||
@@ -892,13 +897,15 @@ export default function AdminSequences() {
|
|||||||
>
|
>
|
||||||
<Eye className="h-4 w-4 text-blue-600" />
|
<Eye className="h-4 w-4 text-blue-600" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{isAdmin && (
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => handleEdit(sequence)}
|
size="sm"
|
||||||
>
|
onClick={() => handleEdit(sequence)}
|
||||||
<Edit className="h-4 w-4" />
|
>
|
||||||
</Button>
|
<Edit className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -920,13 +927,15 @@ export default function AdminSequences() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{isAdmin && (
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => handleDelete(sequence.id)}
|
size="sm"
|
||||||
>
|
onClick={() => handleDelete(sequence.id)}
|
||||||
<Trash2 className="h-4 w-4 text-red-600" />
|
>
|
||||||
</Button>
|
<Trash2 className="h-4 w-4 text-red-600" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
1
todo.md
1
todo.md
@@ -1198,3 +1198,4 @@
|
|||||||
- [ ] Corriger l'erreur dans le fichier ICS qui empêche l'importation dans Outlook
|
- [ ] Corriger l'erreur dans le fichier ICS qui empêche l'importation dans Outlook
|
||||||
- [ ] Ajouter validation date fin > date début dans la boîte de dialogue de modification des séquences
|
- [ ] Ajouter validation date fin > date début dans la boîte de dialogue de modification des séquences
|
||||||
- [x] Ajouter une validation frontend en temps réel dans les champs de date pour afficher un message d'erreur immédiat
|
- [x] Ajouter une validation frontend en temps réel dans les champs de date pour afficher un message d'erreur immédiat
|
||||||
|
- [ ] Restreindre les droits des formateurs (pas de modification/suppression de formations et séquences)
|
||||||
|
|||||||
Reference in New Issue
Block a user