711 lines
33 KiB
TypeScript
711 lines
33 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { useAuth } from '../../../../contexts/AuthContext';
|
|
import ProtectedRoute from '../../../../components/auth/ProtectedRoute';
|
|
import { Card } from 'primereact/card';
|
|
import { Button } from 'primereact/button';
|
|
import { DataTable } from 'primereact/datatable';
|
|
import { Column } from 'primereact/column';
|
|
import { Toast } from 'primereact/toast';
|
|
import { Toolbar } from 'primereact/toolbar';
|
|
import { InputText } from 'primereact/inputtext';
|
|
import { Dialog } from 'primereact/dialog';
|
|
import { Dropdown } from 'primereact/dropdown';
|
|
import { InputTextarea } from 'primereact/inputtextarea';
|
|
import { Tag } from 'primereact/tag';
|
|
import { Checkbox } from 'primereact/checkbox';
|
|
import { Tree } from 'primereact/tree';
|
|
import { TabView, TabPanel } from 'primereact/tabview';
|
|
import { Panel } from 'primereact/panel';
|
|
import { Badge } from 'primereact/badge';
|
|
import { Chip } from 'primereact/chip';
|
|
|
|
interface Permission {
|
|
id: string;
|
|
nom: string;
|
|
description: string;
|
|
module: string;
|
|
action: 'CREATE' | 'READ' | 'UPDATE' | 'DELETE' | 'EXECUTE';
|
|
ressource: string;
|
|
cle: string;
|
|
}
|
|
|
|
interface Role {
|
|
id: string;
|
|
nom: string;
|
|
description: string;
|
|
type: 'SYSTEM' | 'CUSTOM';
|
|
permissions: string[];
|
|
utilisateursAssignes: number;
|
|
dateCreation: Date;
|
|
dateModification: Date;
|
|
actif: boolean;
|
|
couleur: string;
|
|
priorite: number;
|
|
heriteDe?: string;
|
|
}
|
|
|
|
interface ModulePermission {
|
|
module: string;
|
|
permissions: Permission[];
|
|
}
|
|
|
|
const RolesPermissionsPage = () => {
|
|
const [roles, setRoles] = useState<Role[]>([]);
|
|
const [permissions, setPermissions] = useState<Permission[]>([]);
|
|
const [modulesPermissions, setModulesPermissions] = useState<ModulePermission[]>([]);
|
|
const [selectedRoles, setSelectedRoles] = useState<Role[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [globalFilter, setGlobalFilter] = useState('');
|
|
const [roleDialog, setRoleDialog] = useState(false);
|
|
const [permissionDialog, setPermissionDialog] = useState(false);
|
|
const [deleteRoleDialog, setDeleteRoleDialog] = useState(false);
|
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
const [role, setRole] = useState<Role>({
|
|
id: '',
|
|
nom: '',
|
|
description: '',
|
|
type: 'CUSTOM',
|
|
permissions: [],
|
|
utilisateursAssignes: 0,
|
|
dateCreation: new Date(),
|
|
dateModification: new Date(),
|
|
actif: true,
|
|
couleur: '#2196F3',
|
|
priorite: 0
|
|
});
|
|
const [permission, setPermission] = useState<Permission>({
|
|
id: '',
|
|
nom: '',
|
|
description: '',
|
|
module: '',
|
|
action: 'READ',
|
|
ressource: '',
|
|
cle: ''
|
|
});
|
|
const [submitted, setSubmitted] = useState(false);
|
|
const [selectedPermissions, setSelectedPermissions] = useState<string[]>([]);
|
|
const toast = useRef<Toast>(null);
|
|
const dt = useRef<DataTable<Role[]>>(null);
|
|
|
|
const modules = [
|
|
'Dashboard', 'Utilisateurs', 'Clients', 'Chantiers', 'Devis', 'Factures',
|
|
'Stock', 'Planning', 'Rapports', 'Administration', 'Système'
|
|
];
|
|
|
|
const actions = [
|
|
{ label: 'Créer', value: 'CREATE' },
|
|
{ label: 'Lire', value: 'READ' },
|
|
{ label: 'Modifier', value: 'UPDATE' },
|
|
{ label: 'Supprimer', value: 'DELETE' },
|
|
{ label: 'Exécuter', value: 'EXECUTE' }
|
|
];
|
|
|
|
const couleurs = [
|
|
{ label: 'Bleu', value: '#2196F3' },
|
|
{ label: 'Vert', value: '#4CAF50' },
|
|
{ label: 'Orange', value: '#FF9800' },
|
|
{ label: 'Rouge', value: '#F44336' },
|
|
{ label: 'Violet', value: '#9C27B0' },
|
|
{ label: 'Indigo', value: '#3F51B5' }
|
|
];
|
|
|
|
useEffect(() => {
|
|
loadData();
|
|
}, []);
|
|
|
|
const loadData = async () => {
|
|
try {
|
|
setLoading(true);
|
|
|
|
// TODO: Remplacer par un appel API réel pour charger les permissions depuis le backend
|
|
// Exemple: const response = await fetch('/api/admin/permissions');
|
|
// const permissions = await response.json();
|
|
const mockPermissions: Permission[] = [];
|
|
|
|
// TODO: Remplacer par un appel API réel pour charger les rôles depuis le backend
|
|
// Exemple: const response = await fetch('/api/admin/roles');
|
|
// const roles = await response.json();
|
|
const mockRoles: Role[] = [];
|
|
|
|
// Groupement par modules
|
|
const groupedPermissions: ModulePermission[] = modules.map(module => ({
|
|
module,
|
|
permissions: mockPermissions.filter(p => p.module === module)
|
|
})).filter(group => group.permissions.length > 0);
|
|
|
|
setPermissions(mockPermissions);
|
|
setRoles(mockRoles);
|
|
setModulesPermissions(groupedPermissions);
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement:', error);
|
|
toast.current?.show({
|
|
severity: 'error',
|
|
summary: 'Erreur',
|
|
detail: 'Impossible de charger les données',
|
|
life: 3000
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const openNewRole = () => {
|
|
setRole({
|
|
id: '',
|
|
nom: '',
|
|
description: '',
|
|
type: 'CUSTOM',
|
|
permissions: [],
|
|
utilisateursAssignes: 0,
|
|
dateCreation: new Date(),
|
|
dateModification: new Date(),
|
|
actif: true,
|
|
couleur: '#2196F3',
|
|
priorite: 0
|
|
});
|
|
setSelectedPermissions([]);
|
|
setSubmitted(false);
|
|
setRoleDialog(true);
|
|
};
|
|
|
|
const editRole = (role: Role) => {
|
|
setRole({ ...role });
|
|
setSelectedPermissions([...role.permissions]);
|
|
setRoleDialog(true);
|
|
};
|
|
|
|
const confirmDeleteRole = (role: Role) => {
|
|
setRole(role);
|
|
setDeleteRoleDialog(true);
|
|
};
|
|
|
|
const deleteRole = () => {
|
|
if (role.type === 'SYSTEM') {
|
|
toast.current?.show({
|
|
severity: 'error',
|
|
summary: 'Erreur',
|
|
detail: 'Impossible de supprimer un rôle système',
|
|
life: 3000
|
|
});
|
|
return;
|
|
}
|
|
|
|
const updatedRoles = roles.filter(r => r.id !== role.id);
|
|
setRoles(updatedRoles);
|
|
setDeleteRoleDialog(false);
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Rôle supprimé',
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const saveRole = () => {
|
|
setSubmitted(true);
|
|
|
|
if (role.nom.trim() && role.description.trim()) {
|
|
let updatedRoles = [...roles];
|
|
const roleToSave = { ...role, permissions: selectedPermissions };
|
|
|
|
if (role.id) {
|
|
// Mise à jour
|
|
const index = roles.findIndex(r => r.id === role.id);
|
|
updatedRoles[index] = { ...roleToSave, dateModification: new Date() };
|
|
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Rôle mis à jour',
|
|
life: 3000
|
|
});
|
|
} else {
|
|
// Création
|
|
const newRole = {
|
|
...roleToSave,
|
|
id: Date.now().toString(),
|
|
dateCreation: new Date(),
|
|
dateModification: new Date()
|
|
};
|
|
updatedRoles.push(newRole);
|
|
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Rôle créé',
|
|
life: 3000
|
|
});
|
|
}
|
|
|
|
setRoles(updatedRoles);
|
|
setRoleDialog(false);
|
|
}
|
|
};
|
|
|
|
const duplicateRole = (role: Role) => {
|
|
const newRole = {
|
|
...role,
|
|
id: Date.now().toString(),
|
|
nom: `${role.nom} (Copie)`,
|
|
type: 'CUSTOM' as const,
|
|
utilisateursAssignes: 0,
|
|
dateCreation: new Date(),
|
|
dateModification: new Date()
|
|
};
|
|
|
|
setRoles([...roles, newRole]);
|
|
toast.current?.show({
|
|
severity: 'success',
|
|
summary: 'Succès',
|
|
detail: 'Rôle dupliqué',
|
|
life: 3000
|
|
});
|
|
};
|
|
|
|
const onPermissionToggle = (permissionId: string, checked: boolean) => {
|
|
if (checked) {
|
|
setSelectedPermissions([...selectedPermissions, permissionId]);
|
|
} else {
|
|
setSelectedPermissions(selectedPermissions.filter(id => id !== permissionId));
|
|
}
|
|
};
|
|
|
|
const selectAllPermissionsForModule = (module: string, checked: boolean) => {
|
|
const modulePermissions = permissions.filter(p => p.module === module).map(p => p.id);
|
|
|
|
if (checked) {
|
|
const newPermissions = [...selectedPermissions];
|
|
modulePermissions.forEach(permId => {
|
|
if (!newPermissions.includes(permId)) {
|
|
newPermissions.push(permId);
|
|
}
|
|
});
|
|
setSelectedPermissions(newPermissions);
|
|
} else {
|
|
setSelectedPermissions(selectedPermissions.filter(id => !modulePermissions.includes(id)));
|
|
}
|
|
};
|
|
|
|
const exportCSV = () => {
|
|
dt.current?.exportCSV();
|
|
};
|
|
|
|
const leftToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button
|
|
label="Nouveau Rôle"
|
|
icon="pi pi-plus"
|
|
severity="success"
|
|
onClick={openNewRole}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const rightToolbarTemplate = () => {
|
|
return (
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button
|
|
label="Exporter"
|
|
icon="pi pi-upload"
|
|
severity="help"
|
|
onClick={exportCSV}
|
|
/>
|
|
<span className="p-input-icon-left">
|
|
<i className="pi pi-search" />
|
|
<InputText
|
|
type="search"
|
|
placeholder="Rechercher..."
|
|
onInput={(e) => setGlobalFilter(e.currentTarget.value)}
|
|
/>
|
|
</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const actionBodyTemplate = (rowData: Role) => {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<Button
|
|
icon="pi pi-eye"
|
|
rounded
|
|
severity="info"
|
|
onClick={() => editRole(rowData)}
|
|
tooltip="Voir/Modifier"
|
|
/>
|
|
<Button
|
|
icon="pi pi-copy"
|
|
rounded
|
|
severity="success"
|
|
onClick={() => duplicateRole(rowData)}
|
|
tooltip="Dupliquer"
|
|
/>
|
|
{rowData.type === 'CUSTOM' && (
|
|
<Button
|
|
icon="pi pi-trash"
|
|
rounded
|
|
severity="danger"
|
|
onClick={() => confirmDeleteRole(rowData)}
|
|
tooltip="Supprimer"
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const typeBodyTemplate = (rowData: Role) => {
|
|
return (
|
|
<Tag
|
|
value={rowData.type === 'SYSTEM' ? 'Système' : 'Personnalisé'}
|
|
severity={rowData.type === 'SYSTEM' ? 'danger' : 'success'}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const utilisateursBodyTemplate = (rowData: Role) => {
|
|
return <Badge value={rowData.utilisateursAssignes} severity="info" />;
|
|
};
|
|
|
|
const permissionsBodyTemplate = (rowData: Role) => {
|
|
return <Badge value={rowData.permissions.length} />;
|
|
};
|
|
|
|
const couleurBodyTemplate = (rowData: Role) => {
|
|
return (
|
|
<div className="flex align-items-center gap-2">
|
|
<div
|
|
className="w-1rem h-1rem border-circle"
|
|
style={{ backgroundColor: rowData.couleur }}
|
|
></div>
|
|
<span>{rowData.nom}</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const prioriteBodyTemplate = (rowData: Role) => {
|
|
const getPriorityColor = (priority: number) => {
|
|
if (priority >= 90) return 'danger';
|
|
if (priority >= 70) return 'warning';
|
|
if (priority >= 50) return 'info';
|
|
return 'success';
|
|
};
|
|
|
|
return <Tag value={rowData.priorite} severity={getPriorityColor(rowData.priorite)} />;
|
|
};
|
|
|
|
const header = (
|
|
<div className="flex flex-column md:flex-row md:justify-content-between md:align-items-center">
|
|
<h5 className="m-0">Gestion des Rôles et Permissions</h5>
|
|
<div className="flex gap-2">
|
|
<Badge value={roles.filter(r => r.actif).length} className="mr-2" />
|
|
<span className="text-sm">rôles actifs</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="grid">
|
|
<div className="col-12">
|
|
<Card>
|
|
<Toast ref={toast} />
|
|
|
|
<TabView activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)}>
|
|
<TabPanel header="Rôles" leftIcon="pi pi-users mr-2">
|
|
<Toolbar className="mb-4" left={leftToolbarTemplate} right={rightToolbarTemplate} />
|
|
|
|
<DataTable
|
|
ref={dt}
|
|
value={roles}
|
|
selection={selectedRoles}
|
|
onSelectionChange={(e) => setSelectedRoles(e.value)}
|
|
dataKey="id"
|
|
paginator
|
|
rows={10}
|
|
rowsPerPageOptions={[5, 10, 25]}
|
|
className="datatable-responsive"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
currentPageReportTemplate="Affichage de {first} à {last} sur {totalRecords} rôles"
|
|
globalFilter={globalFilter}
|
|
emptyMessage="Aucun rôle trouvé."
|
|
header={header}
|
|
loading={loading}
|
|
>
|
|
<Column selectionMode="multiple" headerStyle={{ width: '4rem' }} />
|
|
<Column field="nom" header="Nom" body={couleurBodyTemplate} sortable />
|
|
<Column field="description" header="Description" />
|
|
<Column field="type" header="Type" body={typeBodyTemplate} sortable />
|
|
<Column field="priorite" header="Priorité" body={prioriteBodyTemplate} sortable />
|
|
<Column field="permissions" header="Permissions" body={permissionsBodyTemplate} />
|
|
<Column field="utilisateursAssignes" header="Utilisateurs" body={utilisateursBodyTemplate} sortable />
|
|
<Column field="dateModification" header="Modifié le" body={(rowData) => rowData.dateModification.toLocaleDateString()} sortable />
|
|
<Column body={actionBodyTemplate} headerStyle={{ minWidth: '8rem' }} />
|
|
</DataTable>
|
|
</TabPanel>
|
|
|
|
<TabPanel header="Permissions" leftIcon="pi pi-key mr-2">
|
|
<div className="grid">
|
|
{modulesPermissions.map((moduleGroup, index) => (
|
|
<div key={index} className="col-12 md:col-6 lg:col-4">
|
|
<Panel header={moduleGroup.module} className="mb-3">
|
|
<div className="grid">
|
|
{moduleGroup.permissions.map((permission) => (
|
|
<div key={permission.id} className="col-12">
|
|
<div className="flex align-items-center justify-content-between p-2 border-bottom-1 surface-border">
|
|
<div>
|
|
<div className="font-semibold">{permission.nom}</div>
|
|
<div className="text-sm text-color-secondary">{permission.description}</div>
|
|
<Chip
|
|
label={permission.action}
|
|
className="mt-1"
|
|
style={{ fontSize: '0.75rem' }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Panel>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</TabPanel>
|
|
|
|
<TabPanel header="Matrice" leftIcon="pi pi-table mr-2">
|
|
<Card title="Matrice Rôles-Permissions">
|
|
<div className="overflow-auto">
|
|
<table className="w-full">
|
|
<thead>
|
|
<tr>
|
|
<th className="text-left p-2 border-bottom-1 surface-border">Permission</th>
|
|
{roles.map(role => (
|
|
<th key={role.id} className="text-center p-2 border-bottom-1 surface-border">
|
|
<div className="flex flex-column align-items-center gap-1">
|
|
<div
|
|
className="w-1rem h-1rem border-circle"
|
|
style={{ backgroundColor: role.couleur }}
|
|
></div>
|
|
<span className="text-xs">{role.nom}</span>
|
|
</div>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{modulesPermissions.map((moduleGroup, moduleIndex) => (
|
|
<React.Fragment key={moduleIndex}>
|
|
<tr>
|
|
<td colSpan={roles.length + 1} className="p-2 font-bold surface-100">
|
|
{moduleGroup.module}
|
|
</td>
|
|
</tr>
|
|
{moduleGroup.permissions.map((permission) => (
|
|
<tr key={permission.id}>
|
|
<td className="p-2 border-bottom-1 surface-border">
|
|
<div className="font-semibold">{permission.nom}</div>
|
|
<div className="text-xs text-color-secondary">{permission.action}</div>
|
|
</td>
|
|
{roles.map(role => (
|
|
<td key={role.id} className="text-center p-2 border-bottom-1 surface-border">
|
|
{role.permissions.includes(permission.id) ? (
|
|
<i className="pi pi-check text-green-500"></i>
|
|
) : (
|
|
<i className="pi pi-times text-red-500"></i>
|
|
)}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</React.Fragment>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</Card>
|
|
</TabPanel>
|
|
</TabView>
|
|
|
|
{/* Dialog rôle */}
|
|
<Dialog
|
|
visible={roleDialog}
|
|
style={{ width: '900px' }}
|
|
header="Détails du rôle"
|
|
modal
|
|
className="p-fluid"
|
|
footer={
|
|
<div className="flex justify-content-end gap-2">
|
|
<Button label="Annuler" icon="pi pi-times" text onClick={() => setRoleDialog(false)} />
|
|
<Button label="Sauvegarder" icon="pi pi-check" onClick={saveRole} />
|
|
</div>
|
|
}
|
|
onHide={() => setRoleDialog(false)}
|
|
>
|
|
<TabView>
|
|
<TabPanel header="Informations générales">
|
|
<div className="formgrid grid">
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="nom">Nom du rôle *</label>
|
|
<InputText
|
|
id="nom"
|
|
value={role.nom}
|
|
onChange={(e) => setRole({...role, nom: e.target.value})}
|
|
required
|
|
className={submitted && !role.nom ? 'p-invalid' : ''}
|
|
/>
|
|
{submitted && !role.nom && <small className="p-invalid">Le nom est requis.</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="couleur">Couleur</label>
|
|
<Dropdown
|
|
id="couleur"
|
|
value={role.couleur}
|
|
options={couleurs}
|
|
onChange={(e) => setRole({...role, couleur: e.value})}
|
|
itemTemplate={(option) => (
|
|
<div className="flex align-items-center gap-2">
|
|
<div
|
|
className="w-1rem h-1rem border-circle"
|
|
style={{ backgroundColor: option.value }}
|
|
></div>
|
|
<span>{option.label}</span>
|
|
</div>
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12">
|
|
<label htmlFor="description">Description *</label>
|
|
<InputTextarea
|
|
id="description"
|
|
value={role.description}
|
|
onChange={(e) => setRole({...role, description: e.target.value})}
|
|
rows={3}
|
|
required
|
|
className={submitted && !role.description ? 'p-invalid' : ''}
|
|
/>
|
|
{submitted && !role.description && <small className="p-invalid">La description est requise.</small>}
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label htmlFor="priorite">Priorité (0-100)</label>
|
|
<InputText
|
|
id="priorite"
|
|
type="number"
|
|
min="0"
|
|
max="100"
|
|
value={role.priorite.toString()}
|
|
onChange={(e) => setRole({...role, priorite: parseInt(e.target.value) || 0})}
|
|
/>
|
|
</div>
|
|
|
|
<div className="field col-12 md:col-6">
|
|
<label>Type de rôle</label>
|
|
<div className="flex align-items-center">
|
|
<Tag
|
|
value={role.type === 'SYSTEM' ? 'Système' : 'Personnalisé'}
|
|
severity={role.type === 'SYSTEM' ? 'danger' : 'success'}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TabPanel>
|
|
|
|
<TabPanel header="Permissions">
|
|
<div className="grid">
|
|
{modulesPermissions.map((moduleGroup, index) => (
|
|
<div key={index} className="col-12">
|
|
<Panel
|
|
header={
|
|
<div className="flex align-items-center justify-content-between w-full">
|
|
<span>{moduleGroup.module}</span>
|
|
<Checkbox
|
|
checked={moduleGroup.permissions.every(p => selectedPermissions.includes(p.id))}
|
|
onChange={(e) => selectAllPermissionsForModule(moduleGroup.module, e.checked || false)}
|
|
/>
|
|
</div>
|
|
}
|
|
className="mb-3"
|
|
>
|
|
<div className="grid">
|
|
{moduleGroup.permissions.map((permission) => (
|
|
<div key={permission.id} className="col-12 md:col-6">
|
|
<div className="flex align-items-center">
|
|
<Checkbox
|
|
checked={selectedPermissions.includes(permission.id)}
|
|
onChange={(e) => onPermissionToggle(permission.id, e.checked || false)}
|
|
/>
|
|
<div className="ml-2">
|
|
<div className="font-semibold">{permission.nom}</div>
|
|
<div className="text-sm text-color-secondary">{permission.description}</div>
|
|
<Chip
|
|
label={permission.action}
|
|
className="mt-1"
|
|
style={{ fontSize: '0.75rem' }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Panel>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-3 p-3 surface-100 border-round">
|
|
<div className="font-semibold mb-2">Résumé des permissions sélectionnées:</div>
|
|
<Badge value={selectedPermissions.length} className="mr-2" />
|
|
<span>permissions sélectionnées</span>
|
|
</div>
|
|
</TabPanel>
|
|
</TabView>
|
|
</Dialog>
|
|
|
|
{/* Dialog suppression */}
|
|
<Dialog
|
|
visible={deleteRoleDialog}
|
|
style={{ width: '450px' }}
|
|
header="Confirmer la suppression"
|
|
modal
|
|
footer={
|
|
<div className="flex justify-content-end gap-2">
|
|
<Button label="Non" icon="pi pi-times" text onClick={() => setDeleteRoleDialog(false)} />
|
|
<Button label="Oui" icon="pi pi-check" onClick={deleteRole} />
|
|
</div>
|
|
}
|
|
onHide={() => setDeleteRoleDialog(false)}
|
|
>
|
|
<div className="flex align-items-center justify-content-center">
|
|
<i className="pi pi-exclamation-triangle mr-3" style={{ fontSize: '2rem' }} />
|
|
{role && (
|
|
<span>
|
|
Êtes-vous sûr de vouloir supprimer le rôle <b>{role.nom}</b> ?
|
|
{role.utilisateursAssignes > 0 && (
|
|
<div className="mt-2">
|
|
<small className="text-orange-600">
|
|
Attention: {role.utilisateursAssignes} utilisateur(s) ont ce rôle assigné.
|
|
</small>
|
|
</div>
|
|
)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</Dialog>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ProtectedRolesPermissionsPage = () => {
|
|
return (
|
|
<ProtectedRoute requiredRoles={['SUPER_ADMIN', 'ADMIN']}>
|
|
<RolesPermissionsPage />
|
|
</ProtectedRoute>
|
|
);
|
|
};
|
|
|
|
export default ProtectedRolesPermissionsPage; |