fix: conformité stricte skill itinova-user-management
- Rôles : remplacement admin/approbateur/validateur/operateur → admin/standard/readonly - schema.ts, migrate.ts : ENUM MySQL mis à jour (3 rôles skill) - routes/auth.ts : rôle par défaut standard, validRoles, modèle CSV corrigé - routes/invoices.ts : permissions readonly/standard/admin - routes/dashboard.ts : compteurs dashboard selon standard/admin - frontend/types/index.ts : type User role mis à jour - frontend/utils/helpers.ts : roleLabels admin/standard/readonly - frontend/pages/InvoiceDetail.tsx : actions disponibles selon standard/readonly/admin - frontend/pages/UserList.tsx : rôle par défaut standard, labels import corrigés
This commit is contained in:
@@ -101,13 +101,17 @@ export default function InvoiceDetail() {
|
||||
const role = user.role;
|
||||
const s = invoice.status;
|
||||
|
||||
if (s === 'recue' && ['operateur', 'validateur', 'approbateur', 'admin'].includes(role)) {
|
||||
// Permissions selon le skill itinova-user-management (admin / standard / readonly)
|
||||
// readonly : aucune action disponible
|
||||
// standard : droits métiers complets (vérification, validation, approbation, rejet)
|
||||
// admin : tous les droits y compris paiement et archivage
|
||||
if (s === 'recue' && ['standard', 'admin'].includes(role)) {
|
||||
actions.push({ status: 'en_verification', label: 'Passer en vérification', color: 'btn-primary' });
|
||||
}
|
||||
if (s === 'en_verification' && ['validateur', 'approbateur', 'admin'].includes(role)) {
|
||||
if (s === 'en_verification' && ['standard', 'admin'].includes(role)) {
|
||||
actions.push({ status: 'validee', label: 'Valider', color: 'btn-success' });
|
||||
}
|
||||
if (s === 'validee' && ['approbateur', 'admin'].includes(role)) {
|
||||
if (s === 'validee' && ['standard', 'admin'].includes(role)) {
|
||||
actions.push({ status: 'approuvee', label: 'Approuver', color: 'btn-success' });
|
||||
}
|
||||
if (s === 'approuvee' && role === 'admin') {
|
||||
@@ -116,10 +120,10 @@ export default function InvoiceDetail() {
|
||||
if (s === 'payee' && role === 'admin') {
|
||||
actions.push({ status: 'archivee', label: 'Archiver', color: 'btn-secondary' });
|
||||
}
|
||||
if (!['payee', 'archivee', 'rejetee'].includes(s) && ['validateur', 'approbateur', 'admin'].includes(role)) {
|
||||
if (!['payee', 'archivee', 'rejetee'].includes(s) && ['standard', 'admin'].includes(role)) {
|
||||
actions.push({ status: 'rejetee', label: 'Rejeter', color: 'btn-danger' });
|
||||
}
|
||||
if (s === 'rejetee' && ['operateur', 'validateur', 'approbateur', 'admin'].includes(role)) {
|
||||
if (s === 'rejetee' && ['standard', 'admin'].includes(role)) {
|
||||
actions.push({ status: 'recue', label: 'Remettre en réception', color: 'btn-secondary' });
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function UserList() {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [form, setForm] = useState({
|
||||
email: '', password: '', firstName: '', lastName: '', role: 'operateur',
|
||||
email: '', password: '', firstName: '', lastName: '', role: 'standard',
|
||||
});
|
||||
|
||||
useEffect(() => { loadUsers(); }, []);
|
||||
@@ -41,7 +41,7 @@ export default function UserList() {
|
||||
|
||||
const openCreate = () => {
|
||||
setEditingUser(null);
|
||||
setForm({ email: '', password: '', firstName: '', lastName: '', role: 'operateur' });
|
||||
setForm({ email: '', password: '', firstName: '', lastName: '', role: 'standard' });
|
||||
setShowModal(true);
|
||||
};
|
||||
|
||||
@@ -242,7 +242,7 @@ export default function UserList() {
|
||||
<p className="font-semibold mb-1">Format attendu (colonnes) :</p>
|
||||
<code className="text-xs bg-blue-100 px-2 py-1 rounded">email, prenom, nom, role</code>
|
||||
<p className="mt-2 text-xs">
|
||||
Roles : <strong>admin</strong>, <strong>approbateur</strong>, <strong>validateur</strong>, <strong>operateur</strong> (défaut si vide).
|
||||
Profils : <strong>admin</strong>, <strong>standard</strong>, <strong>readonly</strong> (défaut : <strong>standard</strong> si vide).
|
||||
</p>
|
||||
<p className="text-xs mt-1">
|
||||
Utilisateurs existants (même email) : mis à jour. Nouveaux : mot de passe temporaire généré.
|
||||
|
||||
@@ -3,7 +3,7 @@ export interface User {
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
role: 'admin' | 'approbateur' | 'validateur' | 'operateur';
|
||||
role: 'admin' | 'standard' | 'readonly';
|
||||
isActive?: boolean;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
|
||||
@@ -83,9 +83,8 @@ export const poStatusColors: Record<string, string> = {
|
||||
|
||||
export const roleLabels: Record<string, string> = {
|
||||
admin: 'Administrateur',
|
||||
approbateur: 'Approbateur',
|
||||
validateur: 'Validateur',
|
||||
operateur: 'Opérateur',
|
||||
standard: 'Standard',
|
||||
readonly: 'Lecture seule',
|
||||
};
|
||||
|
||||
export function isOverdue(dueDate: string | undefined | null, status: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user