refactor(chatbot): ChatbotPlaceholder paramétrable, /agences desktop sur chatbot-reseaux
- ChatbotPlaceholder : props endpoint, titre, placeholderReplie, placeholderSaisie, intro, exemple, fichesTitre, ficheLienModele, rendreMarkdown, messageLimite ; défauts = comportement actuel de l'accueil
- pages/agences.vue : le desktop appelle /api/chatbot-reseaux (le câblage croisé vers le backend Entraide est supprimé), textes orientés structures, rendu markdown, liens /agences#id
- suppression de server/api/chatbot-v2.post.ts et server/utils/vectorSearch.ts (aucun appelant) ; base recherche sémantique par embeddings retrouvable au commit 39602e26
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vcVGz8i4WpZm2dixKUiSD
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
39602e26c7
commit
31edbd37af
@@ -12,7 +12,7 @@
|
||||
role="button"
|
||||
tabindex="0"
|
||||
:aria-expanded="isExpanded"
|
||||
aria-label="Ouvrir l'assistant chatbot"
|
||||
:aria-label="`Ouvrir l'assistant : ${titre}`"
|
||||
@keydown.enter="toggleExpand"
|
||||
@keydown.space.prevent="toggleExpand"
|
||||
>
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
|
||||
<span class="flex-1 text-sm" style="color: var(--nav-text-muted);">
|
||||
{{ isExpanded ? 'Chatbot AEP' : 'Pose une question sur le réseau…' }}
|
||||
{{ isExpanded ? titre : placeholderReplie }}
|
||||
</span>
|
||||
|
||||
<!-- Chevron -->
|
||||
@@ -52,21 +52,22 @@
|
||||
<div class="chatbot-body-inner" ref="messagesContainer">
|
||||
<!-- Onboarding -->
|
||||
<div v-if="messages.length === 0" class="onboarding-bubble">
|
||||
<p>Explore les 120 structures de la carte par la conversation. Je peux t'aider à trouver des collectifs, agences ou réseaux selon ta situation, ta pratique ou tes inspirations du moment.</p>
|
||||
<p class="example">Exemple : "Je cherche des acteurs de la rénovation de maisons individuelles en France, plutôt en milieu rural, avec des approches biosourcées ou low-tech."</p>
|
||||
<p>{{ intro }}</p>
|
||||
<p v-if="exemple" class="example">{{ exemple }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Messages -->
|
||||
<template v-for="(msg, i) in messages" :key="i">
|
||||
<div v-if="msg.role === 'user'" class="user-bubble">{{ msg.content }}</div>
|
||||
<div v-else class="assistant-bubble">
|
||||
<p>{{ msg.content }}</p>
|
||||
<div v-if="rendreMarkdown" v-html="renderMd(msg.content)" />
|
||||
<p v-else>{{ msg.content }}</p>
|
||||
<div v-if="msg.fiches && msg.fiches.length > 0" class="fiches-list">
|
||||
<p class="fiches-title">Fiches recommandees :</p>
|
||||
<p class="fiches-title">{{ fichesTitre }}</p>
|
||||
<a
|
||||
v-for="fiche in msg.fiches"
|
||||
:key="fiche.id"
|
||||
:href="`/fiche/${fiche.id}`"
|
||||
:href="ficheLienModele.replace('{id}', String(fiche.id))"
|
||||
class="fiche-card"
|
||||
>
|
||||
<span class="fiche-nom">{{ fiche.nom }}</span>
|
||||
@@ -106,7 +107,7 @@
|
||||
v-model="inputText"
|
||||
type="text"
|
||||
:disabled="loading"
|
||||
placeholder="Pose ta question…"
|
||||
:placeholder="placeholderSaisie"
|
||||
class="chatbot-input"
|
||||
@keydown.enter.prevent="sendMessage"
|
||||
/>
|
||||
@@ -127,6 +128,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMarkdown } from '~/composables/useMarkdown'
|
||||
const { render: renderMd } = useMarkdown()
|
||||
|
||||
interface FicheReco {
|
||||
id: number | string
|
||||
nom: string
|
||||
@@ -140,6 +144,44 @@ interface ChatMessage {
|
||||
suggestedHashtags?: string[]
|
||||
}
|
||||
|
||||
// Composant unique pour les deux chatbots desktop. Les défauts reproduisent
|
||||
// exactement le comportement de l'accueil (/api/chatbot, backend Entraide).
|
||||
// Les deux backends renvoient { reponse_texte, fiches_recommandees } ; seul
|
||||
// le rendu diffère (texte brut vs markdown, lien de fiche).
|
||||
const props = withDefaults(defineProps<{
|
||||
/** Route serveur appelée (body { question }) */
|
||||
endpoint?: string
|
||||
/** Titre affiché dans l'en-tête déplié */
|
||||
titre?: string
|
||||
/** Texte de l'en-tête replié */
|
||||
placeholderReplie?: string
|
||||
/** Placeholder du champ de saisie */
|
||||
placeholderSaisie?: string
|
||||
/** Texte d'accueil (première bulle) */
|
||||
intro?: string
|
||||
/** Exemple de question, affiché sous l'accueil */
|
||||
exemple?: string
|
||||
/** Intitulé de la liste de fiches/structures citées */
|
||||
fichesTitre?: string
|
||||
/** Modèle de lien vers une fiche, {id} est remplacé */
|
||||
ficheLienModele?: string
|
||||
/** Rendre la réponse en markdown (backend réseaux) plutôt qu'en texte brut */
|
||||
rendreMarkdown?: boolean
|
||||
/** Message affiché quand la limite quotidienne (429) est atteinte */
|
||||
messageLimite?: string
|
||||
}>(), {
|
||||
endpoint: '/api/chatbot',
|
||||
titre: 'Chatbot AEP',
|
||||
placeholderReplie: 'Pose une question sur le réseau…',
|
||||
placeholderSaisie: 'Pose ta question…',
|
||||
intro: "Explore les 120 structures de la carte par la conversation. Je peux t'aider à trouver des collectifs, agences ou réseaux selon ta situation, ta pratique ou tes inspirations du moment.",
|
||||
exemple: 'Exemple : "Je cherche des acteurs de la rénovation de maisons individuelles en France, plutôt en milieu rural, avec des approches biosourcées ou low-tech."',
|
||||
fichesTitre: 'Fiches recommandees :',
|
||||
ficheLienModele: '/fiche/{id}',
|
||||
rendreMarkdown: false,
|
||||
messageLimite: 'Limite de 10 questions par jour atteinte. Reviens demain.',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'highlightOrgs': [ids: (number | string)[]]
|
||||
'applyHashtag': [tag: string]
|
||||
@@ -203,7 +245,7 @@ async function sendMessage() {
|
||||
const res = await $fetch<{
|
||||
reponse_texte: string
|
||||
fiches_recommandees: { id: number | string; nom: string; explication: string }[]
|
||||
}>('/api/chatbot', {
|
||||
}>(props.endpoint, {
|
||||
method: 'POST',
|
||||
body: { question },
|
||||
})
|
||||
@@ -223,7 +265,7 @@ async function sendMessage() {
|
||||
} catch (e: any) {
|
||||
const status = e?.statusCode ?? e?.status
|
||||
if (status === 429) {
|
||||
errorMsg.value = 'Limite de 10 questions par jour atteinte. Reviens demain.'
|
||||
errorMsg.value = props.messageLimite
|
||||
} else if (status === 503) {
|
||||
errorMsg.value = 'Le budget IA mensuel est épuisé. Soutiens NAV sur Liberapay pour continuer.'
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user