import { useState, useEffect, useCallback } from 'react'; import { api } from '../../api.js'; import Modal from '../../components/Modal.jsx'; /* Modale en étapes "Gérer les droits" — Administration > Utilisateurs. * Consolide en un seul endroit ce qui était trois actions séparées du menu * ⋮ (rôle, statut, accès IA) + le nouvel octroi de workspaces : * 1. Information générale (rôle, statut du compte) * 2. Espaces de travail (octroi admin — cf. adminWorkspaces.js / PATCH * /admin/users/:id/workspaces) * 3. Assistant IA (accès individuel, décision admin) * Voir project_workspaces_transformation.md (mémoire projet). */ const STEPS = [ { id: 'general', label: 'Information générale' }, { id: 'workspaces', label: 'Espaces de travail' }, { id: 'ia', label: 'Assistant IA' }, ]; const ROLE_OPTIONS = [{ value: 'user', label: 'Utilisateur' }, { value: 'admin', label: 'Administrateur' }]; const STATUS_OPTIONS = [ { value: 'active', label: 'Actif' }, { value: 'deactivated', label: 'Désactivé' }, { value: 'locked', label: 'Verrouillé' }, ]; function Toggle({ checked, onClick, disabled, title }) { return ( ); } export default function ManageRightsModal({ open, user, currentUserId, aiAvailable, aiUnavailableReason, onClose, onSaved }) { const isSelf = !!user && user.id === currentUserId; const [step, setStep] = useState('general'); const [form, setForm] = useState(null); const [catalog, setCatalog] = useState([]); const [loadingCat, setLoadingCat] = useState(false); const [err, setErr] = useState(null); const [saving, setSaving] = useState(false); useEffect(() => { if (!open || !user) return; setStep('general'); setErr(null); setForm({ role: user.role || 'user', status: user.status || 'active', workspaceIds: user.workspace_ids || [], aiEnabled: !!user.ai_enabled, }); setLoadingCat(true); api.get('/admin/workspaces') .then(setCatalog) .catch(() => setCatalog([])) .finally(() => setLoadingCat(false)); }, [open, user]); const toggleWorkspace = useCallback((id) => { setForm(f => ({ ...f, workspaceIds: f.workspaceIds.includes(id) ? f.workspaceIds.filter(w => w !== id) : [...f.workspaceIds, id], })); }, []); if (!open || !user || !form) return null; const stepIndex = STEPS.findIndex(s => s.id === step); async function handleSave() { setErr(null); setSaving(true); try { const calls = []; if (!isSelf) { if (form.role !== (user.role || 'user')) { calls.push(api.patch(`/admin/users/${user.id}/role`, { role: form.role })); } if (form.status !== (user.status || 'active')) { calls.push(api.patch(`/admin/users/${user.id}/status`, { status: form.status })); } } const origWs = [...(user.workspace_ids || [])].sort().join(','); const newWs = [...form.workspaceIds].sort().join(','); if (origWs !== newWs) { calls.push(api.patch(`/admin/users/${user.id}/workspaces`, { workspaceIds: form.workspaceIds })); } if (form.aiEnabled !== !!user.ai_enabled) { calls.push(api.patch(`/admin/users/${user.id}/ai-access`, { aiEnabled: form.aiEnabled })); } await Promise.all(calls); onSaved(); } catch (e) { setErr(e.message || "Erreur lors de l'enregistrement"); } finally { setSaving(false); } } const identity = user.display_name || user.email; return ( {stepIndex > 0 && ( )} {stepIndex < STEPS.length - 1 && ( )} } > {/* ── Indicateur d'étape (même pattern que InvestissementFormModal) ── */}
Étape {stepIndex + 1} : {STEPS[stepIndex].label}
{STEPS.map((s, i) => (
))}
{step === 'general' && (
{isSelf && (

Vous ne pouvez pas modifier votre propre rôle ni votre propre statut depuis cet écran.

)}
)} {step === 'workspaces' && (

Espaces de travail accordés à cet utilisateur. L'utilisateur pourra ensuite les activer ou les masquer lui-même depuis ses paramètres.

{loadingCat ? (

Chargement…

) : catalog.length === 0 ? (

Aucun espace de travail défini dans le catalogue.

) : (
{catalog.map(w => { const granted = form.workspaceIds.includes(w.id); return (
{w.nom}
{w.description && (
{w.description}
)}
{!w.actif_global && ( indisponible )} toggleWorkspace(w.id)} title={granted ? 'Retirer cet espace de travail' : 'Accorder cet espace de travail'} />
); })}
)} {form.workspaceIds.length === 0 && (

Aucun espace de travail accordé : l'utilisateur n'aura accès à aucune fonctionnalité.

)}
)} {step === 'ia' && (
Accès à l'Assistant IA
{aiAvailable ? "Permet à l'utilisateur d'utiliser le chat de l'Assistant IA." : aiUnavailableReason}
setForm(f => ({ ...f, aiEnabled: !f.aiEnabled }))} title={!aiAvailable ? aiUnavailableReason : (form.aiEnabled ? "Désactiver l'accès à l'Assistant IA" : "Activer l'accès à l'Assistant IA")} />
)} {err &&
{err}
} ); }