Files
astro-site-cerveau/src/pages/manifeste/commander.astro
Jules Neny be7fc09085 feat: PC7 chatbot V1 onglet centre HAUT + endpoint Astro proxy SSR
- ChatbotV2.vue : Vue island, thread chat (input + messages bot/user),
  persistance sessionStorage, bandeau beta '120 fiches AEP, RAG-PE bientot',
  gestion erreurs 429/502/504 ; pas de streaming ni markdown V1
- /api/chatbot.ts : endpoint Astro server proxy POST vers CHATBOT_UPSTREAM
  (default https://aep.trans-former.fr/api/chatbot), timeout 25s,
  body { question, history } -> upstream classique chatbot AEP Mistral Small
- astro.config.mjs : output 'server' + adapter @astrojs/node standalone
  (Astro 6 a supprime mode hybrid ; on opt-in prerender sur les pages)
- Toutes les pages publiques (index, manifeste, manifeste/commander,
  a-propos, mentions-legales) ont 'export const prerender = true'
- ColCentre.astro : remplace ChatbotPlaceholder par ChatbotV2 dans le tab
- .env.example : ajoute CHATBOT_UPSTREAM (V1.5 = switch LightRAG-PE 1 ligne)

Decision V1 : endpoint AEP /api/chatbot (classique, repond bien) au lieu
de /api/chatbot-v2 qui retourne v2_ready=false ('base vectorielle en cours').
Bandeau beta reste valide ; switch v2 quand ready cote AEP via env var.

Note PC8 deploy : Coolify doit booter avec 'node ./dist/server/entry.mjs'
(SSR Node standalone) au lieu de servir dist/client/ static.

Test end-to-end OK : SSR boot port 4399 + curl POST /api/chatbot ->
reponse_texte 800+ chars de l'AEP backend.
2026-05-09 01:22:01 +02:00

115 lines
3.6 KiB
Plaintext

---
export const prerender = true;
import BaseLayout from '../../layouts/BaseLayout.astro';
import HamburgerMenu from '../../components/astro/HamburgerMenu.astro';
---
<BaseLayout
title="Commander la version imprimee - Manifeste AEP"
description="Pre-inscription pour la version imprimee du manifeste Architecture d'Ecologie Politique."
>
<HamburgerMenu />
<main class="min-h-screen bg-white">
<article class="max-w-xl mx-auto px-6 py-16 md:py-24">
<header class="mb-10">
<p class="text-sm uppercase tracking-widest text-neutral-500 mb-3">
Manifeste imprime
</p>
<h1 class="text-3xl md:text-4xl font-semibold text-neutral-900 leading-tight">
Commander la version imprimee
</h1>
</header>
<div class="text-neutral-700 text-base md:text-lg leading-relaxed space-y-5">
<p>
La version imprimee du manifeste sera disponible prochainement ; tirage limite, papier recycle, format A5.
</p>
<p>
Inscrivez-vous pour etre averti-e de la mise en vente. Vous serez prevenu-e en priorite, sans engagement.
</p>
</div>
<!-- Form pre-inscription (V1 stub, V1.1 cable Listmonk) -->
<form
id="manifeste-preinscription"
class="mt-10 flex flex-col gap-3"
novalidate
>
<label for="email" class="text-sm font-medium text-neutral-700">
Votre email
</label>
<input
id="email"
name="email"
type="email"
required
autocomplete="email"
placeholder="prenom@exemple.fr"
class="px-4 py-3 border border-neutral-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:border-neutral-900"
/>
<button
type="submit"
class="mt-2 px-5 py-3 bg-neutral-900 text-white rounded-lg font-medium hover:bg-neutral-700 transition-colors"
>
Etre averti-e
</button>
<p
id="form-feedback"
class="text-sm text-neutral-600 hidden"
aria-live="polite"
></p>
</form>
<p class="mt-8 text-sm text-neutral-500">
<a
href="/manifeste"
class="underline underline-offset-2 hover:text-neutral-900 transition-colors"
>
&larr; Retour au manifeste
</a>
</p>
</article>
</main>
</BaseLayout>
<script>
// V1 stub : capture email en localStorage, V1.1 = POST Listmonk
const form = document.getElementById('manifeste-preinscription') as HTMLFormElement | null;
const feedback = document.getElementById('form-feedback');
form?.addEventListener('submit', (e) => {
e.preventDefault();
const data = new FormData(form);
const email = String(data.get('email') || '').trim();
if (!email || !email.includes('@')) {
if (feedback) {
feedback.textContent = 'Merci de saisir un email valide.';
feedback.classList.remove('hidden');
feedback.classList.add('text-red-600');
}
return;
}
// Stockage local en attendant le cable Listmonk (V1.1)
try {
const existing = JSON.parse(
localStorage.getItem('tf-manifeste-preinscriptions') || '[]'
);
existing.push({ email, ts: Date.now() });
localStorage.setItem('tf-manifeste-preinscriptions', JSON.stringify(existing));
} catch {
// mode prive : on ignore
}
if (feedback) {
feedback.textContent = 'Merci ; vous serez prevenu-e des sa disponibilite.';
feedback.classList.remove('hidden', 'text-red-600');
feedback.classList.add('text-green-700');
}
form.reset();
});
</script>