feat: persist AI chat history to server-side JSON file
Chat history was stored only in sessionStorage, causing member ID and DOB to be lost when the session expired or corrupted — requiring logout/login. Now history is saved to a per-user JSON file on the backend and loaded on mount, so the LLM always has full conversation context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,28 @@ function loadSavedMessages(): Message[] {
|
||||
return [makeMsg("bot", "Hi! What can I help you with today?")];
|
||||
}
|
||||
|
||||
const WELCOME = [makeMsg("bot", "Hi! What can I help you with today?")];
|
||||
|
||||
let saveTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
function saveChatHistoryToServer(msgs: Message[]) {
|
||||
if (saveTimer) clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(() => {
|
||||
const saveable = msgs.filter((m) => !m.isLoading);
|
||||
fetch("/api/ai/chat-history", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("token") ?? ""}` },
|
||||
body: JSON.stringify({ messages: saveable }),
|
||||
}).catch(() => {});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function clearChatHistoryOnServer() {
|
||||
fetch("/api/ai/chat-history", {
|
||||
method: "DELETE",
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem("token") ?? ""}` },
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
export function ChatbotButton() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [step, setStep] = useState<Step>("menu");
|
||||
@@ -173,15 +195,36 @@ export function ChatbotButton() {
|
||||
const freeTextRef = useRef<HTMLTextAreaElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Load chat history from server on first mount (server is source of truth)
|
||||
const serverLoaded = useRef(false);
|
||||
useEffect(() => {
|
||||
if (serverLoaded.current) return;
|
||||
serverLoaded.current = true;
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
fetch("/api/ai/chat-history", {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((data) => {
|
||||
if (Array.isArray(data.messages) && data.messages.length > 0) {
|
||||
setMessages(data.messages);
|
||||
try { sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(data.messages)); } catch {}
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
}, [messages, step]);
|
||||
|
||||
// Persist messages across navigation (cleared on logout)
|
||||
// Persist messages to sessionStorage AND server
|
||||
useEffect(() => {
|
||||
try {
|
||||
const saveable = messages.filter((m) => !m.isLoading);
|
||||
sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(saveable));
|
||||
saveChatHistoryToServer(messages);
|
||||
} catch {}
|
||||
}, [messages]);
|
||||
|
||||
@@ -230,6 +273,7 @@ export function ChatbotButton() {
|
||||
try {
|
||||
sessionStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(fresh));
|
||||
} catch {}
|
||||
clearChatHistoryOnServer();
|
||||
setMessages(fresh);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user