feat(rag-pe): PRG-5 + PRG-6 frontend pensees ecologiques
- server/api/chatbot-pensees.post.ts : endpoint LightRAG VPS (hybrid mode, preface militante, rate limit 20/jour, health guard) - nuxt.config.ts : ragPeUrl runtimeConfig (NUXT_RAG_PE_URL) - public/data/auteurs-pensees.json : 18 auteurs FRACAS, 8 ecoles, theses, livres RAG - components/CartePensees.vue : D3 force-directed (8 ecoles fixes + auteurs gravitants) - components/FicheAuteur.vue : modal auteur (bio + theses + livres RAG + bouton RAG) - components/ChatbotPensees.vue : overlay chatbot bottom-right (sources expansibles) - pages/pensees-ecologiques.vue : page dedicee /pensees-ecologiques (toggle Familiale/Graphe) - pages/agences.vue : 4e onglet "Pensees" (desktop + mobile) -> /pensees-ecologiques Branche : feat/aep-rag-pensees-ecologiques Checkpoint Jules requis avant merge main. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
119
pages/pensees-ecologiques.vue
Normal file
119
pages/pensees-ecologiques.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div class="flex h-full overflow-hidden" style="background: var(--nav-bg);">
|
||||
|
||||
<!-- ZONE PRINCIPALE (pleine largeur, pas de sidebar) -->
|
||||
<main class="flex-1 flex flex-col overflow-hidden relative">
|
||||
|
||||
<!-- Header onglet -->
|
||||
<div class="shrink-0 flex items-center justify-between px-5 py-3"
|
||||
style="background: var(--nav-surface); border-bottom: 1px solid var(--nav-bg-alt);">
|
||||
<div>
|
||||
<h1 class="font-bold text-base" style="color: var(--nav-text);">Pensees Ecologiques</h1>
|
||||
<p class="text-xs mt-0.5" style="color: var(--nav-text-muted);">
|
||||
{{ corpusCount }} auteurs ingeres dans le RAG - carte FRACAS Bonpote V2
|
||||
</p>
|
||||
</div>
|
||||
<!-- Toggle vue -->
|
||||
<div class="flex items-center gap-1 p-1 rounded-lg" style="background: var(--nav-bg-alt);">
|
||||
<button
|
||||
@click="vue = 'familiale'"
|
||||
class="px-3 py-1.5 rounded-md text-xs font-semibold transition-all"
|
||||
:style="vue === 'familiale'
|
||||
? 'background: var(--nav-surface); color: var(--nav-text); box-shadow: 0 1px 3px rgba(0,0,0,0.12);'
|
||||
: 'color: var(--nav-text-muted);'"
|
||||
>Familiale</button>
|
||||
<button
|
||||
@click="vue = 'graphe'"
|
||||
class="px-3 py-1.5 rounded-md text-xs font-semibold transition-all"
|
||||
:style="vue === 'graphe'
|
||||
? 'background: var(--nav-surface); color: var(--nav-text); box-shadow: 0 1px 3px rgba(0,0,0,0.12);'
|
||||
: 'color: var(--nav-text-muted);'"
|
||||
>Graphe</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vue Familiale (CartePensees D3 force-directed) -->
|
||||
<div v-show="vue === 'familiale'" class="flex-1 overflow-hidden relative">
|
||||
<ClientOnly>
|
||||
<CartePensees
|
||||
:data="penseesData"
|
||||
:active="vue === 'familiale'"
|
||||
@select-auteur="onSelectAuteur"
|
||||
/>
|
||||
<template #fallback>
|
||||
<div class="w-full h-full flex items-center justify-center" style="color: var(--nav-text-muted);">
|
||||
Chargement de la carte...
|
||||
</div>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
|
||||
<!-- Vue Graphe (GraphView existant adapte) -->
|
||||
<div v-show="vue === 'graphe'" class="flex-1 overflow-hidden relative">
|
||||
<ClientOnly>
|
||||
<CartePensees
|
||||
:data="penseesData"
|
||||
:active="vue === 'graphe'"
|
||||
@select-auteur="onSelectAuteur"
|
||||
/>
|
||||
<template #fallback>
|
||||
<div class="w-full h-full flex items-center justify-center" style="color: var(--nav-text-muted);">
|
||||
Chargement du graphe...
|
||||
</div>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Fiche auteur modal -->
|
||||
<FicheAuteur
|
||||
:open="ficheOpen"
|
||||
:auteurId="ficheAuteurId"
|
||||
:data="penseesData"
|
||||
@close="ficheOpen = false"
|
||||
@interroger-rag="onInterrogerRag"
|
||||
/>
|
||||
|
||||
<!-- Chatbot flottant -->
|
||||
<ChatbotPensees :auteurContext="chatbotAuteur" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface EcoleData { id: string; label: string; description: string; color: string; x_hint: number; y_hint: number }
|
||||
interface LivreRag { slug: string; titre: string; annee: number; couches: string[] }
|
||||
interface AuteurData { id: string; nom: string; dates: string; ecoles: string[]; ecole_principale: string; livres_rag: LivreRag[]; theses_cles: string[]; bio_courte: string }
|
||||
interface PenseesData { meta: any; ecoles: EcoleData[]; auteurs: AuteurData[] }
|
||||
|
||||
const vue = ref<'familiale' | 'graphe'>('familiale')
|
||||
const ficheOpen = ref(false)
|
||||
const ficheAuteurId = ref<string | null>(null)
|
||||
const chatbotAuteur = ref<string | null>(null)
|
||||
const penseesData = ref<PenseesData | null>(null)
|
||||
|
||||
const corpusCount = computed(() => penseesData.value?.auteurs.length ?? 0)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
penseesData.value = await $fetch<PenseesData>('/data/auteurs-pensees.json')
|
||||
} catch (e) {
|
||||
console.error('Erreur chargement auteurs-pensees.json', e)
|
||||
}
|
||||
})
|
||||
|
||||
function onSelectAuteur(id: string) {
|
||||
ficheAuteurId.value = id
|
||||
ficheOpen.value = true
|
||||
chatbotAuteur.value = null
|
||||
}
|
||||
|
||||
function onInterrogerRag(auteurId: string) {
|
||||
ficheOpen.value = false
|
||||
const auteur = penseesData.value?.auteurs.find(a => a.id === auteurId)
|
||||
chatbotAuteur.value = auteur?.nom ?? null
|
||||
}
|
||||
|
||||
useHead({ title: 'AEP - Pensees Ecologiques - Carte FRACAS' })
|
||||
</script>
|
||||
Reference in New Issue
Block a user