Initial commit
This commit is contained in:
843
app/(main)/administration/page.tsx
Normal file
843
app/(main)/administration/page.tsx
Normal file
@@ -0,0 +1,843 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
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 { TabView, TabPanel } from 'primereact/tabview';
|
||||
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 { ToggleButton } from 'primereact/togglebutton';
|
||||
import { Panel } from 'primereact/panel';
|
||||
import { Divider } from 'primereact/divider';
|
||||
import { Password } from 'primereact/password';
|
||||
import { Checkbox } from 'primereact/checkbox';
|
||||
import { Calendar } from 'primereact/calendar';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { Badge } from 'primereact/badge';
|
||||
import { formatDate } from '../../../utils/formatters';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
nom: string;
|
||||
prenom: string;
|
||||
email: string;
|
||||
telephone: string;
|
||||
role: 'ADMIN' | 'USER' | 'VIEWER';
|
||||
actif: boolean;
|
||||
derniereConnexion: Date;
|
||||
dateCreation: Date;
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
interface SystemLog {
|
||||
id: string;
|
||||
timestamp: Date;
|
||||
niveau: 'INFO' | 'WARNING' | 'ERROR';
|
||||
message: string;
|
||||
utilisateur: string;
|
||||
module: string;
|
||||
action: string;
|
||||
}
|
||||
|
||||
interface SystemConfig {
|
||||
id: string;
|
||||
cle: string;
|
||||
valeur: string;
|
||||
description: string;
|
||||
type: 'string' | 'number' | 'boolean' | 'date';
|
||||
modifiable: boolean;
|
||||
categorie: string;
|
||||
}
|
||||
|
||||
const AdministrationPage = () => {
|
||||
const [users, setUsers] = useState<User[]>([]);
|
||||
const [logs, setLogs] = useState<SystemLog[]>([]);
|
||||
const [config, setConfig] = useState<SystemConfig[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
const [userDialog, setUserDialog] = useState(false);
|
||||
const [configDialog, setConfigDialog] = useState(false);
|
||||
const [deleteUserDialog, setDeleteUserDialog] = useState(false);
|
||||
const [user, setUser] = useState<User>({
|
||||
id: '',
|
||||
nom: '',
|
||||
prenom: '',
|
||||
email: '',
|
||||
telephone: '',
|
||||
role: 'USER',
|
||||
actif: true,
|
||||
derniereConnexion: new Date(),
|
||||
dateCreation: new Date(),
|
||||
permissions: []
|
||||
});
|
||||
const [configItem, setConfigItem] = useState<SystemConfig>({
|
||||
id: '',
|
||||
cle: '',
|
||||
valeur: '',
|
||||
description: '',
|
||||
type: 'string',
|
||||
modifiable: true,
|
||||
categorie: 'general'
|
||||
});
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [password, setPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const toast = useRef<Toast>(null);
|
||||
const dt = useRef<DataTable<User[]>>(null);
|
||||
|
||||
const roles = [
|
||||
{ label: 'Administrateur', value: 'ADMIN' },
|
||||
{ label: 'Utilisateur', value: 'USER' },
|
||||
{ label: 'Lecture seule', value: 'VIEWER' }
|
||||
];
|
||||
|
||||
const configTypes = [
|
||||
{ label: 'Texte', value: 'string' },
|
||||
{ label: 'Nombre', value: 'number' },
|
||||
{ label: 'Booléen', value: 'boolean' },
|
||||
{ label: 'Date', value: 'date' }
|
||||
];
|
||||
|
||||
const configCategories = [
|
||||
{ label: 'Général', value: 'general' },
|
||||
{ label: 'Sécurité', value: 'security' },
|
||||
{ label: 'Email', value: 'email' },
|
||||
{ label: 'Sauvegarde', value: 'backup' },
|
||||
{ label: 'Performance', value: 'performance' }
|
||||
];
|
||||
|
||||
const availablePermissions = [
|
||||
'clients.read',
|
||||
'clients.write',
|
||||
'clients.delete',
|
||||
'chantiers.read',
|
||||
'chantiers.write',
|
||||
'chantiers.delete',
|
||||
'devis.read',
|
||||
'devis.write',
|
||||
'devis.delete',
|
||||
'factures.read',
|
||||
'factures.write',
|
||||
'factures.delete',
|
||||
'stock.read',
|
||||
'stock.write',
|
||||
'stock.delete',
|
||||
'rapports.read',
|
||||
'administration.read',
|
||||
'administration.write'
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Simulation de données
|
||||
const mockUsers: User[] = [
|
||||
{
|
||||
id: '1',
|
||||
nom: 'Admin',
|
||||
prenom: 'BTP Xpress',
|
||||
email: 'admin@btpxpress.com',
|
||||
telephone: '06 12 34 56 78',
|
||||
role: 'ADMIN',
|
||||
actif: true,
|
||||
derniereConnexion: new Date(),
|
||||
dateCreation: new Date('2024-01-01'),
|
||||
permissions: availablePermissions
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
nom: 'Dupont',
|
||||
prenom: 'Jean',
|
||||
email: 'jean.dupont@btpxpress.com',
|
||||
telephone: '06 98 76 54 32',
|
||||
role: 'USER',
|
||||
actif: true,
|
||||
derniereConnexion: new Date(Date.now() - 3600000), // 1 heure
|
||||
dateCreation: new Date('2024-02-01'),
|
||||
permissions: ['clients.read', 'clients.write', 'chantiers.read', 'chantiers.write']
|
||||
}
|
||||
];
|
||||
|
||||
const mockLogs: SystemLog[] = [
|
||||
{
|
||||
id: '1',
|
||||
timestamp: new Date(),
|
||||
niveau: 'INFO',
|
||||
message: 'Utilisateur connecté',
|
||||
utilisateur: 'admin@btpxpress.com',
|
||||
module: 'Auth',
|
||||
action: 'LOGIN'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
timestamp: new Date(Date.now() - 600000), // 10 minutes
|
||||
niveau: 'INFO',
|
||||
message: 'Nouveau client créé',
|
||||
utilisateur: 'jean.dupont@btpxpress.com',
|
||||
module: 'Clients',
|
||||
action: 'CREATE'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
timestamp: new Date(Date.now() - 1800000), // 30 minutes
|
||||
niveau: 'WARNING',
|
||||
message: 'Tentative de connexion échouée',
|
||||
utilisateur: 'unknown@email.com',
|
||||
module: 'Auth',
|
||||
action: 'LOGIN_FAILED'
|
||||
}
|
||||
];
|
||||
|
||||
const mockConfig: SystemConfig[] = [
|
||||
{
|
||||
id: '1',
|
||||
cle: 'app.name',
|
||||
valeur: 'BTP Xpress',
|
||||
description: 'Nom de l\'application',
|
||||
type: 'string',
|
||||
modifiable: true,
|
||||
categorie: 'general'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
cle: 'app.version',
|
||||
valeur: '1.0.0',
|
||||
description: 'Version de l\'application',
|
||||
type: 'string',
|
||||
modifiable: false,
|
||||
categorie: 'general'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
cle: 'auth.session_timeout',
|
||||
valeur: '3600',
|
||||
description: 'Durée de session en secondes',
|
||||
type: 'number',
|
||||
modifiable: true,
|
||||
categorie: 'security'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
cle: 'email.smtp_enabled',
|
||||
valeur: 'true',
|
||||
description: 'Activation du serveur SMTP',
|
||||
type: 'boolean',
|
||||
modifiable: true,
|
||||
categorie: 'email'
|
||||
}
|
||||
];
|
||||
|
||||
setUsers(mockUsers);
|
||||
setLogs(mockLogs);
|
||||
setConfig(mockConfig);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement des données:', error);
|
||||
toast.current?.show({
|
||||
severity: 'error',
|
||||
summary: 'Erreur',
|
||||
detail: 'Impossible de charger les données',
|
||||
life: 3000
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const openNewUser = () => {
|
||||
setUser({
|
||||
id: '',
|
||||
nom: '',
|
||||
prenom: '',
|
||||
email: '',
|
||||
telephone: '',
|
||||
role: 'USER',
|
||||
actif: true,
|
||||
derniereConnexion: new Date(),
|
||||
dateCreation: new Date(),
|
||||
permissions: []
|
||||
});
|
||||
setPassword('');
|
||||
setConfirmPassword('');
|
||||
setSubmitted(false);
|
||||
setUserDialog(true);
|
||||
};
|
||||
|
||||
const editUser = (user: User) => {
|
||||
setUser({ ...user });
|
||||
setPassword('');
|
||||
setConfirmPassword('');
|
||||
setUserDialog(true);
|
||||
};
|
||||
|
||||
const confirmDeleteUser = (user: User) => {
|
||||
setUser(user);
|
||||
setDeleteUserDialog(true);
|
||||
};
|
||||
|
||||
const deleteUser = () => {
|
||||
const updatedUsers = users.filter(u => u.id !== user.id);
|
||||
setUsers(updatedUsers);
|
||||
setDeleteUserDialog(false);
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Utilisateur supprimé',
|
||||
life: 3000
|
||||
});
|
||||
};
|
||||
|
||||
const saveUser = () => {
|
||||
setSubmitted(true);
|
||||
|
||||
if (user.nom.trim() && user.prenom.trim() && user.email.trim()) {
|
||||
if (!user.id && (!password || password !== confirmPassword)) {
|
||||
toast.current?.show({
|
||||
severity: 'error',
|
||||
summary: 'Erreur',
|
||||
detail: 'Mot de passe requis et doit correspondre',
|
||||
life: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedUsers = [...users];
|
||||
|
||||
if (user.id) {
|
||||
// Mise à jour
|
||||
const index = users.findIndex(u => u.id === user.id);
|
||||
updatedUsers[index] = user;
|
||||
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Utilisateur mis à jour',
|
||||
life: 3000
|
||||
});
|
||||
} else {
|
||||
// Création
|
||||
const newUser = {
|
||||
...user,
|
||||
id: Date.now().toString(),
|
||||
dateCreation: new Date()
|
||||
};
|
||||
updatedUsers.push(newUser);
|
||||
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Utilisateur créé',
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
|
||||
setUsers(updatedUsers);
|
||||
setUserDialog(false);
|
||||
}
|
||||
};
|
||||
|
||||
const editConfig = (config: SystemConfig) => {
|
||||
setConfigItem({ ...config });
|
||||
setConfigDialog(true);
|
||||
};
|
||||
|
||||
const saveConfig = () => {
|
||||
const updatedConfig = config.map(c =>
|
||||
c.id === configItem.id ? configItem : c
|
||||
);
|
||||
setConfig(updatedConfig);
|
||||
setConfigDialog(false);
|
||||
|
||||
toast.current?.show({
|
||||
severity: 'success',
|
||||
summary: 'Succès',
|
||||
detail: 'Configuration mise à jour',
|
||||
life: 3000
|
||||
});
|
||||
};
|
||||
|
||||
const onInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, name: string) => {
|
||||
const val = (e.target && e.target.value) || '';
|
||||
let _user = { ...user };
|
||||
(_user as any)[name] = val;
|
||||
setUser(_user);
|
||||
};
|
||||
|
||||
const onDropdownChange = (e: any, name: string) => {
|
||||
let _user = { ...user };
|
||||
(_user as any)[name] = e.value;
|
||||
setUser(_user);
|
||||
};
|
||||
|
||||
const onPermissionChange = (e: any, permission: string) => {
|
||||
let _user = { ...user };
|
||||
if (e.checked) {
|
||||
_user.permissions = [..._user.permissions, permission];
|
||||
} else {
|
||||
_user.permissions = _user.permissions.filter(p => p !== permission);
|
||||
}
|
||||
setUser(_user);
|
||||
};
|
||||
|
||||
const onConfigInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, name: string) => {
|
||||
const val = (e.target && e.target.value) || '';
|
||||
let _config = { ...configItem };
|
||||
(_config as any)[name] = val;
|
||||
setConfigItem(_config);
|
||||
};
|
||||
|
||||
const leftToolbarTemplate = () => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
label="Nouvel utilisateur"
|
||||
icon="pi pi-plus"
|
||||
severity="success"
|
||||
onClick={openNewUser}
|
||||
/>
|
||||
<Button
|
||||
label="Actualiser"
|
||||
icon="pi pi-refresh"
|
||||
onClick={loadData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const rightToolbarTemplate = () => {
|
||||
return (
|
||||
<span className="p-input-icon-left">
|
||||
<i className="pi pi-search" />
|
||||
<InputText
|
||||
type="search"
|
||||
placeholder="Rechercher..."
|
||||
onInput={(e) => setGlobalFilter(e.currentTarget.value)}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const actionBodyTemplate = (rowData: User) => {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
severity="success"
|
||||
onClick={() => editUser(rowData)}
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-trash"
|
||||
rounded
|
||||
severity="warning"
|
||||
onClick={() => confirmDeleteUser(rowData)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const statusBodyTemplate = (rowData: User) => {
|
||||
return (
|
||||
<Tag
|
||||
value={rowData.actif ? 'Actif' : 'Inactif'}
|
||||
severity={rowData.actif ? 'success' : 'danger'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const roleBodyTemplate = (rowData: User) => {
|
||||
const roleLabels = {
|
||||
'ADMIN': 'Administrateur',
|
||||
'USER': 'Utilisateur',
|
||||
'VIEWER': 'Lecture seule'
|
||||
};
|
||||
return roleLabels[rowData.role] || rowData.role;
|
||||
};
|
||||
|
||||
const logLevelBodyTemplate = (rowData: SystemLog) => {
|
||||
const getSeverity = (level: string) => {
|
||||
switch (level) {
|
||||
case 'INFO': return 'info';
|
||||
case 'WARNING': return 'warning';
|
||||
case 'ERROR': return 'danger';
|
||||
default: return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tag
|
||||
value={rowData.niveau}
|
||||
severity={getSeverity(rowData.niveau)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const configActionBodyTemplate = (rowData: SystemConfig) => {
|
||||
return (
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
severity="success"
|
||||
onClick={() => editConfig(rowData)}
|
||||
disabled={!rowData.modifiable}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const configValueBodyTemplate = (rowData: SystemConfig) => {
|
||||
if (rowData.type === 'boolean') {
|
||||
return (
|
||||
<Tag
|
||||
value={rowData.valeur === 'true' ? 'Activé' : 'Désactivé'}
|
||||
severity={rowData.valeur === 'true' ? 'success' : 'danger'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return rowData.valeur;
|
||||
};
|
||||
|
||||
const renderUsersTab = () => {
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<Toolbar className="mb-4" left={leftToolbarTemplate} right={rightToolbarTemplate} />
|
||||
|
||||
<DataTable
|
||||
ref={dt}
|
||||
value={users}
|
||||
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} utilisateurs"
|
||||
globalFilter={globalFilter}
|
||||
emptyMessage="Aucun utilisateur trouvé."
|
||||
loading={loading}
|
||||
>
|
||||
<Column field="prenom" header="Prénom" sortable />
|
||||
<Column field="nom" header="Nom" sortable />
|
||||
<Column field="email" header="Email" sortable />
|
||||
<Column field="role" header="Rôle" body={roleBodyTemplate} sortable />
|
||||
<Column field="actif" header="Statut" body={statusBodyTemplate} />
|
||||
<Column field="derniereConnexion" header="Dernière connexion" body={(rowData) => formatDate(rowData.derniereConnexion)} sortable />
|
||||
<Column body={actionBodyTemplate} />
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderLogsTab = () => {
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<DataTable
|
||||
value={logs}
|
||||
dataKey="id"
|
||||
paginator
|
||||
rows={15}
|
||||
className="datatable-responsive"
|
||||
globalFilter={globalFilter}
|
||||
emptyMessage="Aucun log trouvé."
|
||||
loading={loading}
|
||||
>
|
||||
<Column field="timestamp" header="Date/Heure" body={(rowData) => formatDate(rowData.timestamp)} sortable />
|
||||
<Column field="niveau" header="Niveau" body={logLevelBodyTemplate} sortable />
|
||||
<Column field="module" header="Module" sortable />
|
||||
<Column field="action" header="Action" sortable />
|
||||
<Column field="utilisateur" header="Utilisateur" sortable />
|
||||
<Column field="message" header="Message" />
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderConfigTab = () => {
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<DataTable
|
||||
value={config}
|
||||
dataKey="id"
|
||||
paginator
|
||||
rows={15}
|
||||
className="datatable-responsive"
|
||||
globalFilter={globalFilter}
|
||||
emptyMessage="Aucune configuration trouvée."
|
||||
loading={loading}
|
||||
>
|
||||
<Column field="cle" header="Clé" sortable />
|
||||
<Column field="valeur" header="Valeur" body={configValueBodyTemplate} />
|
||||
<Column field="description" header="Description" />
|
||||
<Column field="categorie" header="Catégorie" sortable />
|
||||
<Column field="type" header="Type" sortable />
|
||||
<Column body={configActionBodyTemplate} />
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const userDialogFooter = (
|
||||
<div className="flex justify-content-end gap-2">
|
||||
<Button label="Annuler" icon="pi pi-times" text onClick={() => setUserDialog(false)} />
|
||||
<Button label="Sauvegarder" icon="pi pi-check" onClick={saveUser} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const configDialogFooter = (
|
||||
<div className="flex justify-content-end gap-2">
|
||||
<Button label="Annuler" icon="pi pi-times" text onClick={() => setConfigDialog(false)} />
|
||||
<Button label="Sauvegarder" icon="pi pi-check" onClick={saveConfig} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const deleteUserDialogFooter = (
|
||||
<div className="flex justify-content-end gap-2">
|
||||
<Button label="Non" icon="pi pi-times" text onClick={() => setDeleteUserDialog(false)} />
|
||||
<Button label="Oui" icon="pi pi-check" onClick={deleteUser} />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<Card>
|
||||
<Toast ref={toast} />
|
||||
|
||||
<TabView activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)}>
|
||||
<TabPanel header="Utilisateurs" leftIcon="pi pi-users mr-2">
|
||||
{renderUsersTab()}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Logs Système" leftIcon="pi pi-list mr-2">
|
||||
{renderLogsTab()}
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Configuration" leftIcon="pi pi-cog mr-2">
|
||||
{renderConfigTab()}
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
|
||||
{/* Dialog utilisateur */}
|
||||
<Dialog
|
||||
visible={userDialog}
|
||||
style={{ width: '700px' }}
|
||||
header="Détails de l'utilisateur"
|
||||
modal
|
||||
className="p-fluid"
|
||||
footer={userDialogFooter}
|
||||
onHide={() => setUserDialog(false)}
|
||||
>
|
||||
<div className="formgrid grid">
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="prenom">Prénom</label>
|
||||
<InputText
|
||||
id="prenom"
|
||||
value={user.prenom}
|
||||
onChange={(e) => onInputChange(e, 'prenom')}
|
||||
required
|
||||
className={submitted && !user.prenom ? 'p-invalid' : ''}
|
||||
/>
|
||||
{submitted && !user.prenom && <small className="p-invalid">Le prénom est requis.</small>}
|
||||
</div>
|
||||
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="nom">Nom</label>
|
||||
<InputText
|
||||
id="nom"
|
||||
value={user.nom}
|
||||
onChange={(e) => onInputChange(e, 'nom')}
|
||||
required
|
||||
className={submitted && !user.nom ? 'p-invalid' : ''}
|
||||
/>
|
||||
{submitted && !user.nom && <small className="p-invalid">Le nom est requis.</small>}
|
||||
</div>
|
||||
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="email">Email</label>
|
||||
<InputText
|
||||
id="email"
|
||||
value={user.email}
|
||||
onChange={(e) => onInputChange(e, 'email')}
|
||||
required
|
||||
className={submitted && !user.email ? 'p-invalid' : ''}
|
||||
/>
|
||||
{submitted && !user.email && <small className="p-invalid">L'email est requis.</small>}
|
||||
</div>
|
||||
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="telephone">Téléphone</label>
|
||||
<InputText
|
||||
id="telephone"
|
||||
value={user.telephone}
|
||||
onChange={(e) => onInputChange(e, 'telephone')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="role">Rôle</label>
|
||||
<Dropdown
|
||||
id="role"
|
||||
value={user.role}
|
||||
options={roles}
|
||||
onChange={(e) => onDropdownChange(e, 'role')}
|
||||
placeholder="Sélectionnez un rôle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="actif">Statut</label>
|
||||
<div className="flex align-items-center">
|
||||
<ToggleButton
|
||||
checked={user.actif}
|
||||
onChange={(e) => setUser({ ...user, actif: e.value })}
|
||||
onLabel="Actif"
|
||||
offLabel="Inactif"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!user.id && (
|
||||
<>
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="password">Mot de passe</label>
|
||||
<Password
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
className={submitted && !password ? 'p-invalid' : ''}
|
||||
/>
|
||||
{submitted && !password && <small className="p-invalid">Le mot de passe est requis.</small>}
|
||||
</div>
|
||||
|
||||
<div className="field col-12 md:col-6">
|
||||
<label htmlFor="confirmPassword">Confirmer le mot de passe</label>
|
||||
<Password
|
||||
id="confirmPassword"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
className={submitted && password !== confirmPassword ? 'p-invalid' : ''}
|
||||
/>
|
||||
{submitted && password !== confirmPassword && <small className="p-invalid">Les mots de passe ne correspondent pas.</small>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="field col-12">
|
||||
<label>Permissions</label>
|
||||
<div className="grid">
|
||||
{availablePermissions.map(permission => (
|
||||
<div key={permission} className="col-12 md:col-6">
|
||||
<div className="flex align-items-center">
|
||||
<Checkbox
|
||||
checked={user.permissions.includes(permission)}
|
||||
onChange={(e) => onPermissionChange(e, permission)}
|
||||
/>
|
||||
<label className="ml-2">{permission}</label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
{/* Dialog configuration */}
|
||||
<Dialog
|
||||
visible={configDialog}
|
||||
style={{ width: '500px' }}
|
||||
header="Configuration"
|
||||
modal
|
||||
className="p-fluid"
|
||||
footer={configDialogFooter}
|
||||
onHide={() => setConfigDialog(false)}
|
||||
>
|
||||
<div className="formgrid grid">
|
||||
<div className="field col-12">
|
||||
<label htmlFor="cle">Clé</label>
|
||||
<InputText
|
||||
id="cle"
|
||||
value={configItem.cle}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field col-12">
|
||||
<label htmlFor="valeur">Valeur</label>
|
||||
{configItem.type === 'boolean' ? (
|
||||
<ToggleButton
|
||||
checked={configItem.valeur === 'true'}
|
||||
onChange={(e) => setConfigItem({ ...configItem, valeur: e.value ? 'true' : 'false' })}
|
||||
onLabel="Activé"
|
||||
offLabel="Désactivé"
|
||||
/>
|
||||
) : configItem.type === 'number' ? (
|
||||
<InputNumber
|
||||
value={parseInt(configItem.valeur)}
|
||||
onValueChange={(e) => setConfigItem({ ...configItem, valeur: e.value?.toString() || '' })}
|
||||
/>
|
||||
) : (
|
||||
<InputText
|
||||
id="valeur"
|
||||
value={configItem.valeur}
|
||||
onChange={(e) => onConfigInputChange(e, 'valeur')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="field col-12">
|
||||
<label htmlFor="description">Description</label>
|
||||
<InputTextarea
|
||||
id="description"
|
||||
value={configItem.description}
|
||||
disabled
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
{/* Dialog suppression utilisateur */}
|
||||
<Dialog
|
||||
visible={deleteUserDialog}
|
||||
style={{ width: '450px' }}
|
||||
header="Confirmer"
|
||||
modal
|
||||
footer={deleteUserDialogFooter}
|
||||
onHide={() => setDeleteUserDialog(false)}
|
||||
>
|
||||
<div className="flex align-items-center justify-content-center">
|
||||
<i className="pi pi-exclamation-triangle mr-3" style={{ fontSize: '2rem' }} />
|
||||
{user && (
|
||||
<span>
|
||||
Êtes-vous sûr de vouloir supprimer l'utilisateur <b>{user.prenom} {user.nom}</b> ?
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Dialog>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdministrationPage;
|
||||
Reference in New Issue
Block a user