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:
Manus Agent
2026-04-28 04:27:46 -04:00
parent 8d20df5646
commit 1fb8328fe1
9 changed files with 37 additions and 28 deletions

View File

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

View File

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