Jul 9 - Chat - save chat sessions

This commit is contained in:
2026-07-09 13:40:56 -04:00
parent 71aef4fe8e
commit 5cf85c564b
12 changed files with 471 additions and 50 deletions
+33 -13
View File
@@ -39,14 +39,24 @@
<div class="col-lg-8">
{# ── Header ── #}
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<div>
<h4 class="mb-0"><i class="bi bi-chat-dots me-2 text-primary"></i>JQC Support Chat</h4>
<small class="text-muted">Ask a question or browse common topics below</small>
</div>
<button class="btn btn-outline-danger btn-sm" data-bs-toggle="modal" data-bs-target="#submitModal">
<i class="bi bi-envelope me-1"></i>Submit to Support
</button>
<div class="d-flex gap-2">
<a href="{{ url_for('support.my_conversations') }}" class="btn btn-outline-secondary btn-sm"
title="View your saved conversations">
<i class="bi bi-clock-history me-1"></i>History
</a>
<a href="{{ url_for('support.chat', new=1) }}" class="btn btn-outline-secondary btn-sm"
title="Start a fresh conversation">
<i class="bi bi-plus-circle me-1"></i>New
</a>
<button class="btn btn-outline-danger btn-sm" data-bs-toggle="modal" data-bs-target="#submitModal">
<i class="bi bi-envelope me-1"></i>Submit to Support
</button>
</div>
</div>
{# ── Chat card ── #}
@@ -153,17 +163,27 @@
const chatWindow = document.getElementById('chat-window');
const chatInput = document.getElementById('chat-input');
const faqSection = document.getElementById('faq-section');
const CSRF_TOKEN = '{{ csrf_token() }}';
// In-memory conversation history sent to the server with each message
let history = [];
// Server-side saved session this chat is being persisted to (null until first send)
let sessionId = {{ chat_session_id | tojson }};
const PRIOR = {{ chat_history | tojson }};
// ── Greeting on page load ──────────────────────────────────────────────
// ── Restore prior conversation, or greet on a fresh session ────────────
var userName = {{ current_user.display_name | tojson }};
appendMessage('ai', "👋 Hi " + userName + "! I'm your JQC support assistant. " +
"I can help you with inspections, issues, reports, and more. " +
"Click a question below or type your own.");
if (PRIOR && PRIOR.length) {
PRIOR.forEach(function (m) {
appendMessage(m.role === 'user' ? 'user' : 'ai', m.content);
history.push({ role: m.role, content: m.content });
});
appendSystem('Continuing your previous conversation. Click "New" above to start a fresh one.');
} else {
appendMessage('ai', "👋 Hi " + userName + "! I'm your JQC support assistant. " +
"I can help you with inspections, issues, reports, and more. " +
"Click a question below or type your own.");
}
// ── Append a message bubble ────────────────────────────────────────────
function appendMessage(role, text) {
@@ -202,8 +222,7 @@
function sendMessage(text) {
if (!text.trim()) return;
// Hide FAQ chips after first interaction
if (faqSection) faqSection.style.display = 'none';
// FAQ chips stay visible for the whole session (kept intentionally).
appendMessage('user', text);
history.push({ role: 'user', content: text });
@@ -219,11 +238,12 @@
'Content-Type': 'application/json',
'X-CSRFToken': CSRF_TOKEN,
},
body: JSON.stringify({ message: text, messages: history }),
body: JSON.stringify({ message: text, messages: history, session_id: sessionId }),
})
.then(function (r) { return r.json(); })
.then(function (data) {
hideTyping();
if (data.session_id) { sessionId = data.session_id; }
const reply = data.reply || 'Sorry, I could not process your request.';
appendMessage('ai', reply);
history.push({ role: 'assistant', content: reply });
@@ -252,7 +272,7 @@
};
window.sendFaq = function (btn) {
btn.disabled = true;
// Chips remain enabled/visible so they can be used throughout the session.
sendMessage(btn.dataset.faq);
};