Spaces:
Runtime error
Runtime error
| const state = { | |
| token: null, | |
| username: null, | |
| }; | |
| const els = { | |
| authPanel: document.getElementById("auth-panel"), | |
| chatPanel: document.getElementById("chat-panel"), | |
| contextPanel: document.getElementById("context-panel"), | |
| loginForm: document.getElementById("login-form"), | |
| chatForm: document.getElementById("chat-form"), | |
| chatInput: document.getElementById("chat-message"), | |
| chatHistory: document.getElementById("chat-history"), | |
| messageTemplate: document.getElementById("message-template"), | |
| status: document.getElementById("status-message"), | |
| sessionUsername: document.getElementById("session-username"), | |
| logoutBtn: document.getElementById("logout-btn"), | |
| accountsList: document.getElementById("accounts-list"), | |
| beneficiariesList: document.getElementById("beneficiaries-list"), | |
| transactionsList: document.getElementById("transactions-list"), | |
| }; | |
| const api = async (path, options = {}) => { | |
| const headers = { | |
| "Content-Type": "application/json", | |
| ...options.headers, | |
| }; | |
| if (state.token) { | |
| headers.Authorization = `Bearer ${state.token}`; | |
| } | |
| const response = await fetch(path, { | |
| ...options, | |
| headers, | |
| }); | |
| const data = await response.json().catch(() => ({})); | |
| if (!response.ok) { | |
| throw new Error(data.error || "Request failed"); | |
| } | |
| return data; | |
| }; | |
| const setStatus = (message, isError = false) => { | |
| els.status.textContent = message || ""; | |
| els.status.style.color = isError ? "#ff7a18" : "#a5ffce"; | |
| }; | |
| const toggleChat = (loggedIn) => { | |
| els.authPanel.classList.toggle("hidden", loggedIn); | |
| els.chatPanel.classList.toggle("hidden", !loggedIn); | |
| els.contextPanel.classList.toggle("hidden", !loggedIn); | |
| if (!loggedIn) { | |
| els.chatHistory.innerHTML = ""; | |
| els.sessionUsername.textContent = ""; | |
| els.chatInput.value = ""; | |
| renderSnapshot({}); | |
| } | |
| }; | |
| const appendMessage = ({ sender, message, timestamp }) => { | |
| const node = els.messageTemplate.content.firstElementChild.cloneNode(true); | |
| node.classList.add(sender); | |
| node.querySelector(".sender").textContent = sender === "user" ? "You" : "Aurora"; | |
| node.querySelector(".content").textContent = message; | |
| node.querySelector("time").textContent = new Date(timestamp || Date.now()).toLocaleTimeString(); | |
| els.chatHistory.appendChild(node); | |
| els.chatHistory.scrollTop = els.chatHistory.scrollHeight; | |
| }; | |
| const renderChat = (history = []) => { | |
| els.chatHistory.innerHTML = ""; | |
| history.forEach((entry) => appendMessage(entry)); | |
| }; | |
| const formatAmount = (value) => | |
| new Intl.NumberFormat("en-US", { | |
| style: "currency", | |
| currency: "USD", | |
| maximumFractionDigits: 2, | |
| }).format(Number(value)); | |
| const renderSnapshot = (snapshot = {}) => { | |
| const accounts = snapshot.accounts || []; | |
| const beneficiaries = snapshot.beneficiaries || []; | |
| const transactions = (snapshot.transactions || []).slice(0, 10); | |
| els.accountsList.innerHTML = | |
| accounts | |
| .map( | |
| (acct) => | |
| `<li><strong>${acct.name}</strong><br />${acct.id} • ${acct.type}<br />${formatAmount( | |
| acct.balance, | |
| )}</li>`, | |
| ) | |
| .join("") || "<li>No accounts available.</li>"; | |
| els.beneficiariesList.innerHTML = | |
| beneficiaries | |
| .map( | |
| (ben) => | |
| `<li><strong>${ben.name}</strong><br />${ben.bank}<br />${ben.account_number}${ | |
| ben.notes ? ` • ${ben.notes}` : "" | |
| }</li>`, | |
| ) | |
| .join("") || "<li>No beneficiaries stored.</li>"; | |
| els.transactionsList.innerHTML = | |
| transactions | |
| .map( | |
| (tx) => | |
| `<li><strong>${tx.description}</strong><br />${tx.account_id} • ${new Date( | |
| tx.timestamp, | |
| ).toLocaleString()}<br />${formatAmount(tx.amount)}</li>`, | |
| ) | |
| .join("") || "<li>No transactions available.</li>"; | |
| }; | |
| els.loginForm.addEventListener("submit", async (event) => { | |
| event.preventDefault(); | |
| const formData = new FormData(event.currentTarget); | |
| const payload = Object.fromEntries(formData.entries()); | |
| try { | |
| const result = await api("/api/login", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| headers: {}, | |
| }); | |
| state.token = result.token; | |
| state.username = result.user.username; | |
| els.sessionUsername.textContent = `Signed in as ${state.username}`; | |
| setStatus("Login successful. Start chatting with Aurora."); | |
| toggleChat(true); | |
| renderSnapshot(result.snapshot || {}); | |
| } catch (error) { | |
| setStatus(error.message, true); | |
| } | |
| }); | |
| els.chatForm.addEventListener("submit", async (event) => { | |
| event.preventDefault(); | |
| if (!state.token) { | |
| setStatus("Please login first.", true); | |
| return; | |
| } | |
| const message = els.chatInput.value.trim(); | |
| if (!message) return; | |
| const optimistic = { | |
| sender: "user", | |
| message, | |
| timestamp: new Date().toISOString(), | |
| }; | |
| appendMessage(optimistic); | |
| els.chatInput.value = ""; | |
| els.chatForm.querySelector("button").disabled = true; | |
| try { | |
| const result = await api("/api/chat", { | |
| method: "POST", | |
| body: JSON.stringify({ message }), | |
| }); | |
| renderChat(result.history); | |
| renderSnapshot(result.snapshot || {}); | |
| } catch (error) { | |
| setStatus(error.message, true); | |
| } finally { | |
| els.chatForm.querySelector("button").disabled = false; | |
| } | |
| }); | |
| els.logoutBtn.addEventListener("click", () => { | |
| state.token = null; | |
| state.username = null; | |
| setStatus("You have logged out."); | |
| toggleChat(false); | |
| }); | |
| // ensure chat area is hidden by default | |
| toggleChat(false); | |