Correctif Bug activation IA
This commit is contained in:
1 parent
b3f5f80d2d
commit
8d104f2058
3 files changed
+55
-5
No files matched your search
@@ -260,8 +260,17 @@ router.patch('/config', (req, res, next) => {
|
||||
body.quotaQuotidien !== undefined ? body.quotaQuotidien : row.quota_quotidien,
|
||||
);
|
||||
|
||||
audit(req, { action: 'ai_config_updated', category: 'ai', actorId: req.user.id, details: body });
|
||||
res.json({ ok: true });
|
||||
// Desactivation globale (transition actif -> inactif) : on retire aussi
|
||||
// l'acces individuel de chaque utilisateur plutot que de le laisser
|
||||
// "vrai" en base mais inoperant, ce qui afficherait un interrupteur
|
||||
// actif mais sans effet dans le panneau Utilisateurs.
|
||||
let usersReset = 0;
|
||||
if (body.actif === false && row.actif) {
|
||||
usersReset = db.prepare('UPDATE users SET ai_enabled = 0 WHERE ai_enabled = 1').run().changes;
|
||||
}
|
||||
|
||||
audit(req, { action: 'ai_config_updated', category: 'ai', actorId: req.user.id, details: { ...body, usersReset } });
|
||||
res.json({ ok: true, usersReset });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
|
||||
@@ -356,8 +356,11 @@ function ConfigSection() {
|
||||
const save = async (next) => {
|
||||
setSaving(true);
|
||||
try {
|
||||
await api.patch('/admin/ai/config', next);
|
||||
setBanner({ ok: true, msg: 'Configuration enregistrée.' });
|
||||
const result = await api.patch('/admin/ai/config', next);
|
||||
const msg = result?.usersReset
|
||||
? `Configuration enregistrée. Accès IA retiré à ${result.usersReset} utilisateur${result.usersReset > 1 ? 's' : ''} (Assistant IA désactivé globalement).`
|
||||
: 'Configuration enregistrée.';
|
||||
setBanner({ ok: true, msg });
|
||||
await load();
|
||||
} catch (e) { setBanner({ ok: false, msg: e.message }); }
|
||||
finally { setSaving(false); }
|
||||
|
||||
@@ -428,6 +428,11 @@ export default function UsersSection({ currentUserId }) {
|
||||
const [openMenu, setOpenMenu] = useState(null);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
// null tant que non charge (voir l'effet ci-dessous) : l'interrupteur
|
||||
// individuel de chaque utilisateur n'a d'effet que si l'IA est activee
|
||||
// globalement ET qu'un fournisseur actif est configure (seule source de
|
||||
// verite : aiAccess.js cote backend).
|
||||
const [aiConfig, setAiConfig] = useState(null);
|
||||
|
||||
const load = useCallback(async (initial = false) => {
|
||||
if (initial) setInitialLoading(true);
|
||||
@@ -437,6 +442,16 @@ export default function UsersSection({ currentUserId }) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => { load(true); }, [load]);
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
Promise.all([api.get('/admin/ai/config'), api.get('/admin/ai/providers')])
|
||||
.then(([cfg, provs]) => {
|
||||
if (cancelled) return;
|
||||
setAiConfig({ actif: !!cfg.actif, hasActiveProvider: provs.some(p => p.actif) });
|
||||
})
|
||||
.catch(() => { if (!cancelled) setAiConfig({ actif: false, hasActiveProvider: false }); });
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
useEffect(() => { setPage(1); }, [search, filterStatus, filterRole]);
|
||||
useEffect(() => {
|
||||
if (!openMenu) return;
|
||||
@@ -458,6 +473,17 @@ export default function UsersSection({ currentUserId }) {
|
||||
const safePage = Math.min(page, totalPages);
|
||||
const paged = filtered.slice((safePage - 1) * pageSize, safePage * pageSize);
|
||||
|
||||
// true tant que aiConfig n'est pas encore charge : le vrai garde-fou reste
|
||||
// cote serveur (getAiAccessStatus), ceci n'est qu'une aide visuelle.
|
||||
const aiAvailable = !aiConfig || (aiConfig.actif && aiConfig.hasActiveProvider);
|
||||
const aiUnavailableReason = !aiConfig
|
||||
? ''
|
||||
: !aiConfig.actif
|
||||
? "L'Assistant IA est desactive globalement. Activez-le dans Assistant IA > Configuration avant d'accorder l'acces a un utilisateur."
|
||||
: !aiConfig.hasActiveProvider
|
||||
? "Aucun fournisseur IA actif n'est configure. Configurez-en un dans Assistant IA > Fournisseurs avant d'accorder l'acces a un utilisateur."
|
||||
: '';
|
||||
|
||||
// ── Actions ───────────────────────────────────────────────────────────
|
||||
const confirm = (title, message, onConfirm, confirmLabel) =>
|
||||
setConfirmAction({ title, message, onConfirm, confirmLabel });
|
||||
@@ -639,6 +665,17 @@ export default function UsersSection({ currentUserId }) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{aiConfig && !aiAvailable && (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8, margin: '0 20px 12px',
|
||||
padding: '8px 14px', borderRadius: 8, fontSize: 12.5,
|
||||
background: 'var(--surface-2)', color: 'var(--text-muted)', border: '1px solid var(--border)',
|
||||
}}>
|
||||
<IcoSparkle />
|
||||
{aiUnavailableReason}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tableau */}
|
||||
{filtered.length === 0 ? (
|
||||
<p style={{ color: 'var(--text-muted)', textAlign: 'center', padding: '40px 20px' }}>
|
||||
@@ -692,7 +729,8 @@ export default function UsersSection({ currentUserId }) {
|
||||
<AiToggle
|
||||
checked={!!u.ai_enabled}
|
||||
onClick={() => toggleAiAccess(u)}
|
||||
title={u.ai_enabled ? "Désactiver l'accès à l'Assistant IA" : "Activer l'accès à l'Assistant IA"}
|
||||
disabled={!aiAvailable}
|
||||
title={!aiAvailable ? aiUnavailableReason : (u.ai_enabled ? "Désactiver l'accès à l'Assistant IA" : "Activer l'accès à l'Assistant IA")}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Reference in new issue
Block a user