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:
Manus
2026-01-23 11:42:31 -05:00
parent 5d40b5be96
commit 953677467a
3 changed files with 59 additions and 42 deletions

View File

@@ -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>
))} ))}

View File

@@ -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>

View File

@@ -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)