Amélioration
No files matched your search
@@ -161,3 +161,65 @@ export async function testConnection({ apiKey, model }) {
|
||||
.trim() || '(réponse vide)';
|
||||
return { reply, durationMs: Date.now() - started, usage: message.usage };
|
||||
}
|
||||
|
||||
/**
|
||||
* lookupInstitution() — recherche web automatique (outil natif Anthropic
|
||||
* "web_search", exécuté côté serveur Anthropic) pour pré-remplir une fiche
|
||||
* institution depuis l'admin (icône "baguette magique", InstitutionModal —
|
||||
* demande Olivier 27/09/26) : au minimum une description succincte et le
|
||||
* pays de domiciliation (code ISO 3166-1 alpha-2, cf. CountrySelect.jsx
|
||||
* côté frontend).
|
||||
*
|
||||
* Contrairement à testConnection() (ping minimal, sans outil), cet appel
|
||||
* n'est pas en streaming : pour un outil serveur, Anthropic exécute la/les
|
||||
* recherche(s) et le tour de lecture des résultats en interne, la réponse
|
||||
* finale (texte, éventuellement précédée de blocs server_tool_use /
|
||||
* web_search_tool_result) arrivant en un seul message — donc pas de boucle
|
||||
* côté client comme dans streamChat(), mais un appel qui peut prendre
|
||||
* plusieurs secondes.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {string} opts.apiKey
|
||||
* @param {string} opts.model
|
||||
* @param {string} opts.nom Nom de l'institution à rechercher
|
||||
* @param {string} [opts.url] URL du site officiel, si déjà connue (aide la recherche)
|
||||
* @returns {Promise<{description:string, domiciliation:string|null, durationMs:number, sourcesCount:number}>}
|
||||
*/
|
||||
export async function lookupInstitution({ apiKey, model, nom, url }) {
|
||||
const anthropic = new Anthropic({ apiKey });
|
||||
const started = Date.now();
|
||||
|
||||
const prompt = [
|
||||
`Recherche sur le web des informations fiables et à jour sur l'institution financière suivante : "${nom}"${url ? ` (site officiel présumé : ${url})` : ''}.`,
|
||||
`C'est potentiellement une banque, néobanque, courtier, assureur, société de gestion ou établissement de paiement.`,
|
||||
`Réponds UNIQUEMENT avec un objet JSON valide, sans aucun texte autour, au format exact :`,
|
||||
`{"description": "<description succincte en français, 1 à 3 phrases : activité, statut/régulateur si pertinent>", "domiciliation": "<code pays ISO 3166-1 alpha-2 du siège social, ex. FR, LU, DE — null si inconnu>"}`,
|
||||
].join('\n');
|
||||
|
||||
const message = await anthropic.messages.create({
|
||||
model,
|
||||
max_tokens: 1024,
|
||||
tools: [{ type: 'web_search_20260318', name: 'web_search', max_uses: 4 }],
|
||||
messages: [{ role: 'user', content: prompt }],
|
||||
});
|
||||
|
||||
const textBlocks = (message.content || []).filter((b) => b.type === 'text').map((b) => b.text);
|
||||
const fullText = textBlocks.join('\n').trim();
|
||||
const sourcesCount = (message.content || []).filter((b) => b.type === 'web_search_tool_result').length;
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
const jsonMatch = fullText.match(/\{[\s\S]*\}/);
|
||||
parsed = JSON.parse(jsonMatch ? jsonMatch[0] : fullText);
|
||||
} catch (e) {
|
||||
throw new Error(`Réponse du modèle non exploitable (JSON attendu) : ${fullText.slice(0, 200) || '(vide)'}`);
|
||||
}
|
||||
|
||||
const description = typeof parsed.description === 'string' ? parsed.description.trim() : '';
|
||||
let domiciliation = typeof parsed.domiciliation === 'string' ? parsed.domiciliation.trim().toUpperCase() : null;
|
||||
if (domiciliation && !/^[A-Z]{2}$/.test(domiciliation)) domiciliation = null;
|
||||
|
||||
if (!description) throw new Error("Le modèle n'a renvoyé aucune description exploitable.");
|
||||
|
||||
return { description, domiciliation, durationMs: Date.now() - started, sourcesCount };
|
||||
}
|
||||
@@ -7,6 +7,10 @@ import { fileURLToPath } from 'url';
|
||||
import sharp from 'sharp';
|
||||
import db from '../db/index.js';
|
||||
import { HttpError } from '../middleware/errorHandler.js';
|
||||
import { getDefaultActiveProvider } from '../ai/providerConfig.js';
|
||||
import { decryptSecret } from '../ai/crypto.js';
|
||||
import { lookupInstitution } from '../ai/providers/anthropic.js';
|
||||
import { audit } from '../utils/audit.js';
|
||||
|
||||
/**
|
||||
* routes/institutionsReferentiel.js — Référentiel admin des institutions financières
|
||||
@@ -281,4 +285,68 @@ router.delete('/:id/icone', (req, res, next) => {
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
|
||||
// ── POST /api/institutions-referentiel/ai-lookup ────────────────────────────
|
||||
// Icône "baguette magique" d'InstitutionModal (demande Olivier 27/09/26) : recherche
|
||||
// web automatique via le connecteur IA (Admin > Intelligence artificielle), pour
|
||||
// pré-remplir au minimum description + domiciliation depuis le nom (et l'URL si
|
||||
// déjà saisie) tapés dans le formulaire — fonctionne aussi bien en création (avant
|
||||
// tout enregistrement) qu'en édition. Réponse toujours 200 : { ok:false, error }
|
||||
// n'est pas une erreur HTTP mais un échec applicatif (IA désactivée, aucun
|
||||
// fournisseur actif, type de fournisseur non pris en charge...) — même précédent
|
||||
// que POST /providers/:id/test dans adminAi.js.
|
||||
const AiLookupSchema = z.object({
|
||||
nom: z.string().min(1).max(200),
|
||||
url: z.string().url().optional().or(z.literal('')).nullable(),
|
||||
});
|
||||
|
||||
router.post('/ai-lookup', async (req, res, next) => {
|
||||
try {
|
||||
const { nom, url } = AiLookupSchema.parse(req.body);
|
||||
|
||||
const config = db.prepare('SELECT actif FROM ai_config WHERE id = 1').get();
|
||||
if (!config?.actif) {
|
||||
return res.json({ ok: false, error: "L'Assistant IA n'est pas activé (Admin > Intelligence artificielle)." });
|
||||
}
|
||||
|
||||
const provider = getDefaultActiveProvider();
|
||||
if (!provider) {
|
||||
return res.json({ ok: false, error: "Aucun fournisseur IA actif n'est configuré." });
|
||||
}
|
||||
// Seul le fournisseur "anthropic" (natif) expose l'outil de recherche web
|
||||
// serveur utilisé par lookupInstitution() — un fournisseur openai_compatible
|
||||
// n'a aucune garantie d'accès web réel selon le modèle/service derrière
|
||||
// base_url, on préfère donc refuser explicitement plutôt que de renvoyer une
|
||||
// réponse non vérifiée présentée comme une recherche internet.
|
||||
if (provider.type !== 'anthropic') {
|
||||
return res.json({
|
||||
ok: false,
|
||||
error: `La recherche web automatique n'est prise en charge que pour un fournisseur Anthropic (natif) — le fournisseur actif ("${provider.nom}") est de type "${provider.type}".`,
|
||||
});
|
||||
}
|
||||
|
||||
let apiKey;
|
||||
try {
|
||||
apiKey = decryptSecret(provider.api_key_chiffree);
|
||||
} catch (e) {
|
||||
throw new HttpError(500, `Impossible de déchiffrer la clé API : ${e.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await lookupInstitution({ apiKey, model: provider.modele, nom, url: url || undefined });
|
||||
audit(req, {
|
||||
action: 'institution_ai_lookup', category: 'ai', actorId: req.user.id,
|
||||
details: { nom, ok: true, dureeMs: result.durationMs, sourcesCount: result.sourcesCount },
|
||||
});
|
||||
res.json({ ok: true, description: result.description, domiciliation: result.domiciliation, dureeMs: result.durationMs });
|
||||
} catch (e) {
|
||||
audit(req, {
|
||||
action: 'institution_ai_lookup', category: 'ai', actorId: req.user.id,
|
||||
details: { nom, ok: false, error: e.message },
|
||||
});
|
||||
res.json({ ok: false, error: e.message });
|
||||
}
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) by Marsupilami -->
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg789"
|
||||
version="1.1"
|
||||
width="1024"
|
||||
height="404"
|
||||
viewBox="-3.2371623 -3.2371623 289.9450246 114.3797346">
|
||||
<defs
|
||||
id="defs786" />
|
||||
<path
|
||||
id="path668"
|
||||
d="m 275.83984,0 c -4.79,0 -7.62891,3.35 -7.62891,7 0,3.65 2.57891,7 7.62891,7 5.05,0 7.63086,-3.32 7.63086,-7 0,-3.63 -2.84086,-7 -7.63086,-7 z M 24.4707,1 0,59.7793 H 13.85156 L 18.64062,48.34961 H 40.36133 L 45.15039,59.7793 H 58.99023 L 34.4707,1 Z m 225.09961,0 v 15.61914 a 25.56,25.56 0 0 0 -6.5,-0.85938 c -8.43,0 -13,4.00016 -15,5.91016 a 22.84,22.84 0 0 0 -6.36914,15.89062 c -0.07,14.13 7.65023,19.73024 11.49023,21.49024 a 17.61,17.61 0 0 0 8.26953,1.63867 15.33,15.33 0 0 0 8.75,-3.50976 l 0.39063,2.60937 h 12 V 1 Z m -47.23438,14.7539 c -2.15572,-0.005 -4.10437,0.32063 -5.48437,0.79688 a 16.93,16.93 0 0 0 -6.38086,3.75 l -0.5293,-3.52149 h -12 V 59.7793 h 13.13868 V 30.11914 a 20.65,20.65 0 0 1 3.56054,-2.50977 8,8 0 0 1 7.43946,-0.17969 c 2.3,1.29 3.36132,4.32 3.36132,8 v 24.39063 h 13.13868 V 34.52929 c 0.01,-9.26 -3.39969,-13.38015 -5.42969,-15.16015 -3.05625,-2.68125 -7.22158,-3.60684 -10.81446,-3.61524 z m -92.52539,0.006 a 27.77,27.77 0 0 0 -14.18945,4.45117 c -2.91,-3 -10.04992,-5.74164 -15.91992,-3.68164 a 17,17 0 0 0 -6.38086,3.75 L 72.86132,16.76967 H 60.79101 v 43 H 73.93164 V 30.0001 a 14.17,14.17 0 0 1 3.47851,-2.41016 c 1.47,-0.71 5.10047,-1.99906 7.73047,0.21094 2,1.71 2.11914,5.7389 2.11914,6.8789 v 25.08009 h 13 V 34.0001 a 29.55,29.55 0 0 0 -0.42968,-4.66993 24.52,24.52 0 0 1 3.86132,-2 c 3,-1.33 6.39953,-0.67062 7.51953,0.10938 2.41,1.68 2.65039,5.4907 2.65039,7.2207 v 25.08008 h 13 V 34.19932 c 0,-4.7 -0.66085,-10.61946 -4.88085,-14.43946 -4.22,-3.82 -10.79993,-4 -12.16993,-4 z m 21.91993,1.00977 v 25.42969 c 0,4.77 0.85085,10.00062 4.13085,13.64062 3.28001,3.64 8.55993,5.10953 13.41993,4.76954 a 16.84,16.84 0 0 0 6.43945,-1.78907 14.55,14.55 0 0 0 3.86914,-2.7207 l 0.56055,3.68945 h 12 V 16.78916 l 0.041,-0.0195 h -13.14062 v 29.79101 a 22.79,22.79 0 0 1 -3.42969,2.13868 10.18,10.18 0 0 1 -7.34961,0.43164 c -3,-1.14 -3.41015,-6.00078 -3.41016,-8.05078 V 16.76966 Z m 137.58007,0 V 59.7794 h 13.05078 V 16.76963 Z m -239.83984,2.09957 7,18.33008 h -14 z M 244.00586,26.629 a 9.39,9.39 0 0 1 0.61523,0.002 15.47,15.47 0 0 1 5,0.88867 l -0.0508,19.25977 a 7.4,7.4 0 0 1 -3.19922,2.25 c -4.54,1.86 -10.34055,-0.23 -11.56055,-8 a 15.26,15.26 0 0 1 0.63086,-8.14844 9.39,9.39 0 0 1 8.56446,-6.25195 z"
|
||||
style="fill:#0c1c49" />
|
||||
<rect
|
||||
style="fill:#0c1c49"
|
||||
id="rect682"
|
||||
height="11.07"
|
||||
width="281.85999"
|
||||
y="70.849991"
|
||||
x="0.51071012"
|
||||
class="cls-1" />
|
||||
<path
|
||||
id="path686"
|
||||
d="m 81.51562,92.6875 a 7.22,7.22 0 0 0 -1.43554,0.14258 c -1.94,0.29 -3.81969,1.44976 -4.17969,3.50976 a 3.94,3.94 0 0 0 1.2207,3.75 11.13,11.13 0 0 0 3.85938,1.73047 7.75,7.75 0 0 1 1.7207,0.70899 1.18,1.18 0 0 1 0.48047,1.55078 c -0.29,0.53 -1.09063,0.66015 -1.64063,0.66015 a 3.76,3.76 0 0 1 -2,-0.4707 4.41,4.41 0 0 1 -1.26953,-1.04687 l -2.61133,2.01757 a 6.27,6.27 0 0 0 2.30078,1.83985 c 3.27,1.62 9.23969,0.70039 9.17969,-3.84961 a 4.09,4.09 0 0 0 -1.7207,-3.41016 15.09,15.09 0 0 0 -4,-1.59961 3.5,3.5 0 0 1 -1.61914,-0.88086 1,1 0 0 1 0.34961,-1.45898 2.91,2.91 0 0 1 1.91015,-0.18164 3.37,3.37 0 0 1 1.75,1 4.59,4.59 0 0 1 0.43946,0.54101 l 2.61132,-2 A 5.56,5.56 0 0 0 86.08008,94.32031 7.22,7.22 0 0 0 81.51562,92.6875 Z m -3.24414,10.53516 0.0684,-0.0527 -0.0996,0.0195 a 4.41,4.41 0 0 0 0.0312,0.0332 z M 94.23437,92.6875 a 7.22,7.22 0 0 0 -1.43359,0.14258 c -1.94,0.29 -3.81969,1.44976 -4.17969,3.50976 a 3.94,3.94 0 0 0 1.21875,3.75 11.13,11.13 0 0 0 3.86133,1.73047 7.75,7.75 0 0 1 1.71875,0.70899 1.18,1.18 0 0 1 0.48047,1.55078 c -0.29,0.54 -1.09063,0.66015 -1.64063,0.66015 a 3.75,3.75 0 0 1 -2,-0.4707 4.4,4.4 0 0 1 -1.27344,-1.05273 l -2.60546,2.01367 a 6.27,6.27 0 0 0 2.30078,1.83984 c 3.27,1.62 9.23969,0.70039 9.17968,-3.84961 a 4.09,4.09 0 0 0 -1.7207,-3.41015 15.06,15.06 0 0 0 -4,-1.59961 3.49,3.49 0 0 1 -1.61914,-0.88086 1,1 0 0 1 0.34961,-1.46094 2.9,2.9 0 0 1 1.91016,-0.17969 3.36,3.36 0 0 1 1.75,1 4.51,4.51 0 0 1 0.43945,0.54102 l 2.60938,-2 a 5.55,5.55 0 0 0 -0.7793,-0.91016 7.22,7.22 0 0 0 -4.56641,-1.63281 z m -3.24805,10.5293 0.0742,-0.0566 -0.0996,0.0293 a 4.4,4.4 0 0 0 0.0254,0.0274 z M 202.25976,92.74609 c -0.26877,0.01 -0.53633,0.032 -0.79883,0.0645 a 6.15,6.15 0 0 0 -4.83984,3.3789 9.58,9.58 0 0 0 -0.87109,4.78125 8,8 0 0 0 1.45117,4.42969 c 1.55,2.04 3.83039,2.64047 6.40039,2.48047 A 16.05,16.05 0 0 0 208.30078,107 v -7.41992 h -5.7207 v 2.88086 l 0.0215,0.0586 h 2.06836 v 2.12109 a 5.73,5.73 0 0 1 -2.71875,-0.10156 c -0.61,-0.25 -2.43141,-1.14937 -2.19141,-4.85937 a 4.41,4.41 0 0 1 1.15039,-3 2.4,2.4 0 0 1 3.08985,-0.21875 3.82,3.82 0 0 1 0.93164,1.28906 l 3.25,-1.66016 a 6.52,6.52 0 0 0 -0.85156,-1.39062 c -1.21626,-1.505 -3.18893,-2.02149 -5.07032,-1.95313 z M 65.21093,93 l -5.64062,14.51953 3.96094,0.0312 1,-2.5 h 4.8789 l 0.94141,2.5 h 4.17969 L 68.85156,93 Z m 36.98047,0 v 14.51953 h 10.79883 v -3.34961 h -6.90039 v -2.36914 h 5.25 v -3.07031 h -5.25 v -2.40039 h 6.45117 V 93 Z m 11.93946,0 v 3.33008 h 4.07031 v 11.18945 h 3.92969 V 96.33008 h 4 V 93 Z m 18.33984,0 v 14.51953 l 3.74023,0.01 v -7.13868 l 3,7.22852 h 1.58985 l 2.90039,-7.18945 v 7.09961 h 3.76953 V 93 h -4 l -3.38086,8.65039 L 136.4707,93 Z m 22.4707,0 -5.64062,14.51953 3.9707,0.0312 1,-2.5 h 4.86914 l 0.93946,2.5 h 4.17968 L 158.58008,93 Z m 11.10938,0 v 14.51953 l 3.81054,0.01 V 99.32054 L 175,107.52953 h 3.55078 V 93 h -3.83008 v 7.68945 L 169.99023,93 Z m 19.92969,0 -5.65039,14.51953 3.9707,0.0312 1,-2.5 h 4.88086 l 0.93945,2.5 h 4.17969 L 189.62109,93 Z m 25.13085,0 v 14.51953 h 10.8086 v -3.34961 h -6.89844 v -2.36914 h 5.25 v -3.07031 h -5.25 v -2.40039 h 6.44922 V 93 Z m 13.24024,0 v 14.51953 l 3.81836,0.01 v -7.13868 l 3,7.22852 h 1.55078 l 2.90039,-7.18945 v 7.09961 h 3.76953 V 93 h -4 L 231.9707,101.65039 228.35156,93 Z m 18.30859,0 v 14.51953 h 10.81055 v -3.34961 h -6.91016 v -2.36914 h 5.25 v -3.07031 h -5.25 v -2.40039 h 6.44922 V 93 Z m 13.25,0 v 14.51953 l 3.81055,0.01 v -8.20899 l 5.14062,8.20899 h 3.54883 V 93 h -3.83007 v 7.68945 L 259.85156,93 Z m 14.44141,0 v 3.33008 h 4.06836 v 11.18945 h 3.93164 V 96.33008 h 4 V 93 Z m -203.40039,4.63086 1.57031,4.41992 h -3.14062 z m 89.73047,0 1.56836,4.41992 h -3.13868 z m 31.03906,0 1.57031,4.41992 h -3.14062 z"
|
||||
style="fill:#009fe3" />
|
||||
</svg>
|
||||
<!-- version: 20171223, original size: 283.4707 107.90541, border: 3% -->
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 342 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 888 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 187 KiB |
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 1000 269.23077"
|
||||
version="1.1"
|
||||
id="svg331"
|
||||
sodipodi:docname="A.svg"
|
||||
width="1000"
|
||||
height="269.23077"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata337">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs335" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#111111"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview333"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.15913462"
|
||||
inkscape:cx="1067.5699"
|
||||
inkscape:cy="-1171.2951"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg331" />
|
||||
<path
|
||||
d="m 290,123.84615 c 0,7.69231 0.76923,15.38462 2.30769,21.53847 1.53846,6.92307 3.84616,12.30769 7.69231,17.6923 3.84615,4.61539 7.69231,9.23077 13.84615,11.53846 6.15385,2.3077 12.3077,4.61539 20,4.61539 7.69231,0 13.84616,-1.53846 20.76923,-3.84615 6.15385,-2.3077 10.76924,-5.38462 14.61539,-7.69231 V 71.53846 C 365.38462,70 361.53846,70 356.92308,69.23077 c -4.61539,-0.76923 -10.76923,-0.76923 -18.46154,-0.76923 -14.61539,0 -26.15385,4.61538 -35.38462,14.61538 C 294.61538,92.30769 290,106.15385 290,123.84615 Z M 266.92308,90 c 3.84615,-10 8.46154,-18.46154 15.38461,-25.38461 6.92308,-6.92308 14.61539,-12.3077 23.84616,-16.15385 9.23077,-2.30769 20,-4.61539 31.53846,-4.61539 12.30769,0 23.84615,0.76924 33.84615,3.07693 10,2.30769 17.69231,3.84615 23.84616,5.38461 v 212.30769 l -26.92308,4.61539 v -76.92308 c -3.07692,2.30769 -8.46154,4.61539 -15.38462,6.92308 -6.92307,2.30769 -14.61538,3.84615 -23.07692,3.84615 -10.76923,0 -20.76923,-2.30769 -29.23077,-6.15384 -8.46154,-3.84616 -15.38461,-9.23077 -21.53846,-16.15385 -6.15385,-6.92308 -10,-15.38461 -13.07692,-25.38461 -3.07693,-10 -4.61539,-20 -4.61539,-31.53847 0,-12.30769 1.53846,-23.84615 5.38462,-33.84615 z m 300.76923,33.07692 c 0,12.3077 -1.53846,23.07693 -5.38462,33.07693 -3.84615,10 -8.46154,18.46153 -14.61538,25.38461 -6.15385,6.92308 -13.84616,12.30769 -22.30769,16.15385 -8.46154,3.84615 -18.46154,5.38461 -28.46154,5.38461 -10,0 -20,-1.53846 -28.46154,-5.38461 C 460,193.84615 452.30769,188.46154 446.15385,181.53846 440,174.61538 434.61538,166.15385 431.53846,156.15385 c -3.84615,-10 -5.38461,-20.76923 -5.38461,-33.07693 0,-11.53846 1.53846,-23.07692 5.38461,-32.30769 3.84616,-10 8.46154,-18.46154 14.61539,-25.38461 6.15384,-6.92308 13.84615,-12.3077 22.30769,-16.15385 8.46154,-3.84615 18.46154,-5.38462 28.46154,-5.38462 10,0 20,1.53847 28.46154,5.38462 8.46153,3.84615 16.15384,9.23077 22.30769,16.15385 6.15384,6.92307 11.53846,15.38461 14.61538,25.38461 3.84616,10 5.38462,20.76923 5.38462,32.30769 z m -28.46154,0 c 0,-16.92307 -3.84615,-30.76923 -11.53846,-40.76923 -7.69231,-10 -18.46154,-15.38461 -31.53846,-15.38461 -13.07693,0 -23.84616,5.38461 -31.53847,15.38461 -7.6923,10 -10.76923,23.84616 -10.76923,40.76923 0,16.92308 3.84616,30.76923 11.53847,40.76923 7.6923,10 18.46153,15.38462 31.53846,15.38462 13.07692,0 23.84615,-5.38462 31.53846,-15.38462 6.92308,-10 10.76923,-23.07692 10.76923,-40.76923 z m 58.46154,-71.53846 c 6.15384,-1.53846 14.61538,-3.07692 24.61538,-4.61538 10,-1.53846 22.30769,-2.30769 35.38462,-2.30769 12.30769,0 22.30769,1.53846 30,5.38461 7.69231,3.07692 14.61538,8.46154 19.23077,14.61539 5.38461,4.61538 8.46154,12.30769 10.76923,20.76923 2.30769,8.46153 3.07692,17.6923 3.07692,28.46153 v 85.38462 H 693.84615 V 120 c 0,-9.23077 -0.76923,-17.69231 -1.53846,-23.84615 C 691.53846,90 689.23077,83.84615 686.15385,80 683.07692,76.15385 679.23077,73.07692 674.61538,70.76923 670,68.46154 663.84615,67.69231 656.15385,67.69231 h -9.23077 c 0,0 -6.92308,0.76923 -10,1.53846 C 633.84615,70 631.53846,70 629.23077,70 c -2.30769,0.76923 -3.84615,0.76923 -4.61539,0.76923 V 199.23077 H 597.69231 Z M 782.30769,46.92308 H 840 V 70 h -57.69231 v 70 c 0,7.69231 0.76923,13.84615 1.53846,19.23077 1.53847,4.61538 3.07693,9.23077 5.38462,11.53846 2.30769,3.07692 5.38461,4.61539 8.46154,6.15385 3.84615,1.53846 7.69231,1.53846 12.30769,1.53846 8.46154,0 14.61538,-0.76923 20,-3.07692 4.61538,-1.53847 8.46154,-3.07693 10,-3.84616 l 5.38462,22.30769 c -3.07693,1.53847 -7.69231,3.07693 -14.61539,5.38462 -6.92308,2.30769 -14.61538,3.07692 -23.07692,3.07692 -10,0 -19.23077,-1.53846 -25.38462,-3.84615 -6.92307,-2.30769 -12.30769,-6.92308 -16.15384,-11.53846 -3.84616,-5.38462 -6.92308,-11.53846 -8.46154,-19.23077 C 756.15385,160 755.38462,150.76923 755.38462,140.76923 V 4.61539 L 782.30769,0 Z M 1000,123.07692 c 0,12.3077 -1.53846,23.07693 -5.38462,33.07693 -3.84615,10 -8.46153,18.46153 -14.61538,25.38461 -6.15385,6.92308 -13.84615,12.30769 -22.30769,16.15385 -8.46154,3.84615 -18.46154,5.38461 -28.46154,5.38461 -10,0 -20,-1.53846 -28.46154,-5.38461 -8.46154,-3.84616 -16.15385,-9.23077 -22.30769,-16.15385 -6.15385,-6.92308 -11.53846,-15.38461 -14.61539,-25.38461 -3.84615,-10 -5.38461,-20.76923 -5.38461,-33.07693 0,-11.53846 1.53846,-23.07692 5.38461,-32.30769 3.84616,-10 8.46154,-18.46154 14.61539,-25.38461 6.15384,-6.92308 13.84615,-12.3077 22.30769,-16.15385 8.46154,-3.84615 18.46154,-5.38462 28.46154,-5.38462 10,0 20,1.53847 28.46154,5.38462 8.46154,3.84615 16.15384,9.23077 22.30769,16.15385 6.15385,6.92307 11.53846,15.38461 14.61538,25.38461 3.84616,10 5.38462,20.76923 5.38462,32.30769 z m -28.46154,0 c 0,-16.92307 -3.84615,-30.76923 -11.53846,-40.76923 -7.69231,-10 -18.46154,-15.38461 -31.53846,-15.38461 -13.07692,0 -23.84616,5.38461 -31.53846,15.38461 -7.69231,10 -11.53846,23.84616 -11.53846,40.76923 0,16.92308 3.84615,30.76923 11.53846,40.76923 7.6923,10 18.46154,15.38462 31.53846,15.38462 13.07692,0 23.84615,-5.38462 31.53846,-15.38462 7.69231,-Line truncated
|
||||
class="fill-logo-1"
|
||||
id="path319"
|
||||
inkscape:connector-curvature="0"
|
||||
style="stroke-width:7.69230747;fill:#262a3e;fill-opacity:1" />
|
||||
<g
|
||||
id="g329"
|
||||
transform="scale(7.6923077)"
|
||||
style="fill:#6b5aed;fill-opacity:1">
|
||||
<path
|
||||
d="m 3.5,11.8 c 2.1,2.1 4.9,3.3 7.8,3.5 C 11.1,12.4 9.9,9.6 7.8,7.5 5.7,5.4 3,4.2 0,4 0.2,6.9 1.4,9.7 3.5,11.8 Z"
|
||||
class="fill-logo-2"
|
||||
id="path321"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#6b5aed;fill-opacity:1" />
|
||||
<path
|
||||
d="m 20.5,11.8 c -2.1,2.1 -4.9,3.3 -7.8,3.5 0.2,-2.9 1.4,-5.7 3.5,-7.8 C 18.2,5.4 21,4.2 24,4 23.8,6.9 22.6,9.7 20.5,11.8 Z"
|
||||
class="fill-logo-2"
|
||||
id="path323"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#6b5aed;fill-opacity:1" />
|
||||
<path
|
||||
d="m 20.5,20.1 c -2.1,-2.1 -4.9,-3.3 -7.8,-3.5 0.2,2.9 1.4,5.7 3.5,7.8 2.1,2.1 4.9,3.3 7.8,3.5 -0.2,-3 -1.4,-5.8 -3.5,-7.8 z"
|
||||
class="fill-logo-2"
|
||||
id="path325"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#6b5aed;fill-opacity:1" />
|
||||
<path
|
||||
d="M 3.5,20.1 C 5.6,18 8.4,16.8 11.4,16.6 11.2,19.5 10,22.3 7.9,24.4 5.7,26.5 3,27.7 0,27.9 c 0.2,-3 1.4,-5.8 3.5,-7.8 z"
|
||||
class="fill-logo-2"
|
||||
id="path327"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#6b5aed;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -46,6 +46,10 @@ function IconChevronLeft() {
|
||||
return <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"/></svg>;
|
||||
}
|
||||
|
||||
function IconWand() {
|
||||
return <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M21 16h-4"/><path d="M11 3H9"/></svg>;
|
||||
}
|
||||
|
||||
/* ── Modale de création/édition d'une institution ────────────────
|
||||
Formulaire simple à un seul écran (contrairement au RefModal des plateformes, à 5 étapes) :
|
||||
une institution financière n'a qu'une poignée de champs, un wizard serait disproportionné. */
|
||||
@@ -54,6 +58,9 @@ function InstitutionModal({ row, onClose, onSaved }) {
|
||||
const [categories, setCategories] = useState([]);
|
||||
const [err, setErr] = useState(null);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [aiAvailable, setAiAvailable] = useState(false);
|
||||
const [aiLoading, setAiLoading] = useState(false);
|
||||
const [aiMsg, setAiMsg] = useState(null); // { ok:boolean, text:string }
|
||||
|
||||
const [form, setForm] = useState({
|
||||
nom: row?.nom || '',
|
||||
@@ -67,8 +74,40 @@ function InstitutionModal({ row, onClose, onSaved }) {
|
||||
api.get('/ref-categories-institutions').then(setCategories).catch(() => {});
|
||||
}, []);
|
||||
|
||||
// Icône « baguette magique » (demande Olivier 27/09/26) : visible seulement si le
|
||||
// connecteur IA est activé (Admin > Intelligence artificielle) — la disponibilité d'un
|
||||
// fournisseur compatible (Anthropic natif) est vérifiée côté serveur lors de l'appel.
|
||||
useEffect(() => {
|
||||
api.get('/admin/ai/config').then(cfg => setAiAvailable(!!cfg?.actif)).catch(() => setAiAvailable(false));
|
||||
}, []);
|
||||
|
||||
const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
|
||||
|
||||
const handleAiLookup = async () => {
|
||||
if (!form.nom.trim()) { setAiMsg({ ok: false, text: 'Renseignez le nom avant de lancer la recherche.' }); return; }
|
||||
setAiLoading(true); setAiMsg(null);
|
||||
try {
|
||||
const r = await api.post('/institutions-referentiel/ai-lookup', {
|
||||
nom: form.nom.trim(),
|
||||
url: form.url.trim() || undefined,
|
||||
});
|
||||
if (r.ok) {
|
||||
setForm(f => ({
|
||||
...f,
|
||||
description: r.description || f.description,
|
||||
domiciliation: r.domiciliation || f.domiciliation,
|
||||
}));
|
||||
setAiMsg({ ok: true, text: "Description et domiciliation renseignées automatiquement — vérifiez avant d'enregistrer." });
|
||||
} else {
|
||||
setAiMsg({ ok: false, text: r.error || 'Recherche IA indisponible.' });
|
||||
}
|
||||
} catch (ex) {
|
||||
setAiMsg({ ok: false, text: ex.message });
|
||||
} finally {
|
||||
setAiLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!form.nom.trim()) { setErr('Le nom est requis.'); return; }
|
||||
@@ -113,6 +152,21 @@ function InstitutionModal({ row, onClose, onSaved }) {
|
||||
<label>URL</label>
|
||||
<input value={form.url} onChange={e => set('url', e.target.value)} placeholder="https://…" />
|
||||
</div>
|
||||
{aiAvailable && (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||
<button type="button" onClick={handleAiLookup} disabled={aiLoading || !form.nom.trim()}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, padding: '5px 10px',
|
||||
border: '1px solid var(--border)', borderRadius: 6,
|
||||
cursor: (aiLoading || !form.nom.trim()) ? 'not-allowed' : 'pointer',
|
||||
background: 'var(--surface-2)', color: 'var(--text)', opacity: (aiLoading || !form.nom.trim()) ? 0.6 : 1 }}
|
||||
title="Rechercher automatiquement sur le web (description, domiciliation) via l'Assistant IA">
|
||||
<IconWand /> {aiLoading ? 'Recherche en cours…' : "Compléter avec l'IA"}
|
||||
</button>
|
||||
{aiMsg && (
|
||||
<span style={{ fontSize: 12, color: aiMsg.ok ? '#16a34a' : '#dc2626' }}>{aiMsg.text}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label>Domiciliation</label>
|
||||
<CountrySelect value={form.domiciliation} onChange={code => set('domiciliation', code)} showCode />
|
||||
|
||||