nvtitan's picture
Upload 39 files
8e2e76b verified
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);