aiBenintchie / static /index.html
nvikou's picture
Upload index.html
4df89f1 verified
Raw
History Blame Contribute Delete
12 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Romuald WADAGNI — Assistant de communication</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23008751'/%3E%3Ctext x='16' y='22' text-anchor='middle' font-size='18' font-family='serif' fill='white'%3ERW%3C/text%3E%3C/svg%3E" />
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f4f6f8;
height: 100dvh;
display: flex;
flex-direction: column;
align-items: center;
padding: 12px 24px;
}
/* ── Header ── */
.header {
display: flex;
height: 72px;
width: 100%;
max-width: 1400px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
flex-shrink: 0;
margin-bottom: 12px;
}
.header-left {
background: #008751;
flex: 2;
display: flex;
align-items: center;
padding: 0 18px;
gap: 14px;
}
.header-left img {
width: 54px;
height: 54px;
border-radius: 50%;
object-fit: cover;
border: 2px solid white;
flex-shrink: 0;
}
.header-name { color: white; font-weight: 700; font-size: 15px; letter-spacing: 0.3px; }
.header-badge {
background: white;
color: #008751;
font-weight: 600;
font-size: 11px;
padding: 2px 10px;
border-radius: 999px;
margin-top: 3px;
display: inline-block;
}
.header-right {
flex: 1;
display: flex;
flex-direction: column;
}
.header-right .yellow { background: #FCD116; flex: 1; }
.header-right .red { background: #E8112D; flex: 1; }
/* ── Chat window ── */
.chat-window {
width: 100%;
max-width: 1400px;
flex: 1;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.messages {
flex: 1;
overflow-y: auto;
padding: 20px 16px;
display: flex;
flex-direction: column;
gap: 12px;
scroll-behavior: smooth;
}
/* ── Bubbles ── */
.bubble-wrap {
display: flex;
align-items: flex-end;
gap: 8px;
}
.bubble-wrap.user { flex-direction: row-reverse; }
.bubble-wrap.bot { flex-direction: row; }
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
object-fit: cover;
}
.avatar-placeholder {
width: 32px;
height: 32px;
border-radius: 50%;
background: #008751;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
.bubble {
max-width: 55%;
padding: 10px 14px;
border-radius: 18px;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
.bubble.user {
background: #008751;
color: white;
border-bottom-right-radius: 4px;
}
.bubble.bot {
background: #f0f2f5;
color: #1a1a1a;
border-bottom-left-radius: 4px;
}
/* ── Typing indicator ── */
.typing {
display: flex;
align-items: center;
gap: 5px;
padding: 10px 14px;
background: #f0f2f5;
border-radius: 18px;
border-bottom-left-radius: 4px;
width: fit-content;
}
.typing span {
width: 7px; height: 7px;
background: #888;
border-radius: 50%;
animation: bounce 1.2s infinite;
}
.typing span:nth-child(2) { animation-delay: 0.2s; }
.typing span:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-6px); }
}
/* ── Input bar ── */
.input-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border-top: 1px solid #e8e8e8;
background: white;
}
.input-bar textarea {
flex: 1;
resize: none;
border: 1px solid #ddd;
border-radius: 22px;
padding: 9px 16px;
font-size: 14px;
font-family: inherit;
outline: none;
max-height: 120px;
line-height: 1.4;
transition: border-color 0.2s;
}
.input-bar textarea:focus { border-color: #008751; }
.input-bar button {
width: 40px; height: 40px;
border: none;
background: #008751;
color: white;
border-radius: 50%;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background 0.2s;
}
.input-bar button:hover:not(:disabled) { background: #006640; }
.input-bar button:disabled { background: #b0c4b8; cursor: not-allowed; }
</style>
</head>
<body>
<!-- Header drapeau béninois -->
<div class="header">
<div class="header-left">
<img id="photoImg" src="/photo" alt="Romuald WADAGNI" onerror="this.style.display='none'" />
<div>
<div class="header-name">Romuald WADAGNI</div>
<span class="header-badge">Assistant de communication</span>
</div>
</div>
<div class="header-right">
<div class="yellow"></div>
<div class="red"></div>
</div>
</div>
<!-- Fenêtre de chat -->
<div class="chat-window">
<div class="messages" id="messages">
<!-- Message de bienvenue -->
<div class="bubble-wrap bot">
<img class="avatar" src="/photo" alt="bot" onerror="this.outerHTML='<div class=\'avatar-placeholder\'>🇧🇯</div>'" />
<div class="bubble bot">Bonjour ! Je suis l'assistant IA de communication de Son Excellence le Président Romuald WADAGNI. Comment puis-je vous aider ?</div>
</div>
<!-- Bloc consentement -->
<div id="consent-block" style="
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px 14px;
margin: 4px 16px 0 48px;
background: rgba(0, 135, 81, 0.06);
border: 1px solid rgba(0, 135, 81, 0.2);
border-radius: 10px;
font-size: 0.82rem;
color: #555;
line-height: 1.5;
">
<input type="checkbox" id="consentCheckbox" style="
width: 18px;
height: 18px;
accent-color: #008751;
cursor: pointer;
flex-shrink: 0;
margin-top: 2px;
" />
<label for="consentCheckbox" style="cursor: pointer;">
J'ai lu et j'accepte la
<a href="/privacy.html" target="_blank" style="color: #008751; text-decoration: underline; font-weight: 600;">politique de confidentialité</a>
et le traitement de mes données.
</label>
</div>
</div>
<!-- Barre de saisie -->
<div class="input-bar">
<textarea
id="userInput"
placeholder="Posez votre question…"
rows="1"
></textarea>
<button id="sendBtn" title="Envoyer">&#9654;</button>
</div>
</div>
<script>
const messagesEl = document.getElementById("messages");
const inputEl = document.getElementById("userInput");
const sendBtn = document.getElementById("sendBtn");
// Historique au format [{role, content}]
const history = [];
function scrollBottom() {
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function addBubble(role, text) {
const wrap = document.createElement("div");
wrap.className = `bubble-wrap ${role === "user" ? "user" : "bot"}`;
if (role !== "user") {
const av = document.createElement("img");
av.className = "avatar";
av.src = "/photo";
av.alt = "bot";
av.onerror = () => { av.outerHTML = '<div class="avatar-placeholder">🇧🇯</div>'; };
wrap.appendChild(av);
}
const bubble = document.createElement("div");
bubble.className = `bubble ${role === "user" ? "user" : "bot"}`;
bubble.textContent = text;
wrap.appendChild(bubble);
messagesEl.appendChild(wrap);
scrollBottom();
return bubble;
}
function addTyping() {
const wrap = document.createElement("div");
wrap.className = "bubble-wrap bot";
wrap.id = "typing-wrap";
const av = document.createElement("img");
av.className = "avatar";
av.src = "/photo";
av.alt = "bot";
av.onerror = () => { av.outerHTML = '<div class="avatar-placeholder">🇧🇯</div>'; };
wrap.appendChild(av);
const t = document.createElement("div");
t.className = "typing";
t.innerHTML = "<span></span><span></span><span></span>";
wrap.appendChild(t);
messagesEl.appendChild(wrap);
scrollBottom();
}
function removeTyping() {
const el = document.getElementById("typing-wrap");
if (el) el.remove();
}
async function sendMessage() {
const text = inputEl.value.trim();
if (!text) return;
// ── Vérification consentement côté client ──
const consentCheckbox = document.getElementById("consentCheckbox");
if (!consentCheckbox.checked) {
const block = document.getElementById("consent-block");
block.style.border = "1px solid #E8112D";
block.style.background = "rgba(232, 17, 45, 0.06)";
setTimeout(() => {
block.style.border = "1px solid rgba(0, 135, 81, 0.2)";
block.style.background = "rgba(0, 135, 81, 0.06)";
}, 3000);
addBubble("bot", "⚠️ Veuillez accepter la politique de confidentialité avant d'envoyer votre message.");
return;
}
inputEl.value = "";
inputEl.style.height = "auto";
sendBtn.disabled = true;
addBubble("user", text);
addTyping();
try {
const res = await fetch("/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: text, history: [...history], consent: consentCheckbox.checked }),
});
if (!res.ok) throw new Error(`Erreur serveur : ${res.status}`);
const data = await res.json();
removeTyping();
if (data.consent_required) {
addBubble("bot", data.response);
return;
}
addBubble("bot", data.response);
// Mettre à jour l'historique
history.push({ role: "user", content: text });
history.push({ role: "assistant", content: data.response });
} catch (err) {
removeTyping();
addBubble("bot", "Désolé, une erreur s'est produite. Veuillez réessayer.");
console.error(err);
} finally {
sendBtn.disabled = false;
inputEl.focus();
}
}
sendBtn.addEventListener("click", sendMessage);
inputEl.addEventListener("keydown", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Auto-resize du textarea
inputEl.addEventListener("input", () => {
inputEl.style.height = "auto";
inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + "px";
});
</script>
</body>
</html>