/** * Bank App - Vanilla JS Implementation matching WIP UI */ //============================================================================= // #region LIBRARY - DOM Helper //============================================================================= function h(tag, props = {}, ...children) { const el = document.createElement(tag); if (props) { Object.entries(props).forEach(([key, value]) => { if (key.startsWith("on") && typeof value === "function") { el.addEventListener(key.substring(2).toLowerCase(), value); } else if (key === "className") { el.className = value; } else if (key === "style" && typeof value === "object") { Object.assign(el.style, value); } else if ( key === "disabled" || key === "checked" || key === "selected" || key === "readonly" ) { if (value) el[key] = true; } else { el.setAttribute(key, value); } }); } children.forEach((child) => { if (typeof child === "string" || typeof child === "number") { el.appendChild(document.createTextNode(child)); } else if (child instanceof Node) { el.appendChild(child); } else if (Array.isArray(child)) { child.forEach((c) => { if (c instanceof Node) el.appendChild(c); }); } }); return el; } let _rootContainer = null; let _rootComponent = null; function render(component, container) { _rootContainer = container; _rootComponent = component; _render(); } function _render() { if (_rootContainer && _rootComponent) { _rootContainer.innerHTML = ""; _rootContainer.appendChild(_rootComponent()); } } //============================================================================= // #region UI COMPONENTS //============================================================================= function Navbar() { const state = store.getState(); const uid = state.uid || "Unknown"; return h( "nav", { className: "navbar" }, h( "div", { className: "navbar-inner" }, h( "div", { className: "navbar-brand" }, h( "span", { className: "navbar-title" }, "FDIC - Global Financial Network", ), ), h( "div", { className: "navbar-profile" }, h( "div", { className: "profile-info" }, h("span", { className: "profile-label" }, "Account"), h("span", { className: "profile-id" }, uid), ), ), ), ); } function WindowTitleBar() { return h( "div", { className: "window-titlebar" }, h( "div", { className: "window-titlebar-brand" }, h( "span", { className: "window-titlebar-kicker" }, "FDIC Workspace", ), h( "span", { className: "window-titlebar-title" }, "Global Financial Network", ), ), h( "div", { className: "window-titlebar-controls" }, h( "button", { type: "button", className: "window-control-btn", disabled: true, title: "Minimize unavailable", "aria-label": "Minimize unavailable", }, "-", ), h( "button", { type: "button", className: "window-control-btn", disabled: true, title: "Maximize unavailable", "aria-label": "Maximize unavailable", }, "[ ]", ), h( "button", { type: "button", className: "window-control-btn is-close", onClick: () => sendEvent("bank::close", {}), title: "Close", "aria-label": "Close banking interface", }, "X", ), ), ); } function TransactionHistory() { const state = store.getState(); const transactions = state.transactions || []; return h( "div", { className: "card" }, h( "h3", { style: { textAlign: "left", borderBottom: "1px solid var(--border)", paddingBottom: "1rem", marginBottom: "1rem", }, }, "Recent Transactions", ), transactions.length === 0 ? h( "p", { style: { color: "var(--text-muted)" } }, "No transactions yet", ) : h( "ul", { style: { listStyle: "none", padding: 0, margin: 0 } }, transactions.slice(0, 10).map((tx) => { const isCredit = tx.type === "Deposit"; return h( "li", { style: { display: "flex", justifyContent: "space-between", padding: "0.75rem 0", borderBottom: "1px solid var(--bg-surface-hover)", }, }, h( "div", { style: { textAlign: "left" } }, h( "div", { style: { fontWeight: "500" } }, tx.type, ), h( "div", { style: { fontSize: "0.85rem", color: "var(--text-muted)", }, }, tx.date, ), ), h( "div", { style: { fontWeight: "700", color: isCredit ? "#10b981" : "#ef4444", }, }, (isCredit ? "+" : "-") + "$" + Math.abs(tx.amount).toLocaleString(), ), ); }), ), ); } function DepositWithdrawForm() { const state = store.getState(); const bankBalance = state.accounts.bank; const cashBalance = state.accounts.cash; const getAmount = () => { const input = document.getElementById("deposit-withdraw-amount"); return parseFloat(input?.value) || 0; }; const clearInput = () => { const input = document.getElementById("deposit-withdraw-amount"); if (input) input.value = ""; }; const handleDeposit = () => { const amount = getAmount(); if (!amount || amount <= 0) { console.log("Please enter a valid amount"); return; } if (amount > cashBalance) { console.log("Insufficient cash"); return; } sendEvent("bank::deposit", { amount }); store.dispatch(deposit(amount)); clearInput(); }; const handleWithdraw = () => { const amount = getAmount(); if (!amount || amount <= 0) { console.log("Please enter a valid amount"); return; } if (amount > bankBalance) { console.log("Insufficient funds"); return; } sendEvent("bank::withdraw", { amount }); store.dispatch(withdraw(amount)); clearInput(); }; return h( "div", { className: "card" }, h("h2", null, "Deposit / Withdraw"), h( "div", { className: "balance-info" }, h( "div", { className: "balance-info-item" }, h("span", { className: "balance-info-label" }, "Cash"), h( "span", { className: "balance-info-value cash" }, "$" + cashBalance.toLocaleString(), ), ), h( "div", { className: "balance-info-item" }, h("span", { className: "balance-info-label" }, "Bank"), h( "span", { className: "balance-info-value" }, "$" + bankBalance.toLocaleString(), ), ), ), h( "div", { className: "deposit-withdraw-form" }, h("input", { id: "deposit-withdraw-amount", type: "number", placeholder: "Enter amount...", min: "1", }), h( "div", { className: "deposit-withdraw-buttons" }, h( "button", { onClick: handleDeposit, disabled: cashBalance <= 0 }, "Deposit", ), h( "button", { onClick: handleWithdraw, disabled: bankBalance <= 0 }, "Withdraw", ), ), ), ); } function TransferForm() { const state = store.getState(); const players = state.accounts.players || {}; const currentUid = state.uid; const handleSubmit = (e) => { e.preventDefault(); const formData = new FormData(e.target); const amount = parseFloat(formData.get("amount")); const playerId = formData.get("playerId"); if (!amount || amount <= 0) { console.log("Please enter a valid amount"); return; } const currentState = store.getState(); if (!playerId) { console.log("Please select a recipient"); return; } if (amount > currentState.accounts.bank) { console.log("Insufficient funds"); return; } sendEvent("bank::transfer", { from: "bank", amount, target: playerId }); store.dispatch(transfer("bank", amount, "player")); e.target.reset(); }; // Build player options const playerOptions = [ h( "option", { value: "", disabled: true, selected: true }, "Select player...", ), ]; Object.keys(players).forEach((uid) => { if (uid !== currentUid && players[uid]?.name) { playerOptions.push(h("option", { value: uid }, players[uid].name)); } }); return h( "div", { className: "card" }, h("h2", null, "Wire Transfer"), h( "form", { onSubmit: handleSubmit }, h( "div", null, h("label", null, "Recipient"), h("select", { name: "playerId" }, playerOptions), ), h( "div", null, h("label", null, "Amount"), h("input", { name: "amount", type: "number", placeholder: "0.00", }), ), h("button", { type: "submit" }, "Send Funds"), ), ); } function BankDashboard() { const state = store.getState(); const bankBalance = state.accounts.bank; const earnings = state.accounts.earnings; return h( "div", { className: "content" }, h( "div", { className: "card", style: { gridColumn: "span 2" } }, h( "h2", { style: { fontSize: "1.2rem", color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", }, }, "Account Balance", ), h( "div", { style: { fontSize: "2.8rem", fontWeight: "800", color: "var(--primary-hover)", margin: "1rem 0", }, }, "$" + bankBalance.toLocaleString(), ), h( "div", { style: { textAlign: "center", color: "var(--text-muted)", fontSize: "1.1rem", marginBottom: "1rem", }, }, "Pending: ", h( "span", { style: { color: "#fbbf24", fontWeight: "bold" } }, "$" + earnings.toLocaleString(), ), ), h( "div", { className: "deposit-earnings-button" }, h( "button", { onClick: () => { sendEvent("bank::depositEarnings", { amount: earnings, }); store.dispatch(depositEarnings(earnings)); }, disabled: earnings <= 0, style: { width: "25%" }, }, "Deposit Earnings", ), ), ), DepositWithdrawForm(), TransferForm(), h("div", { style: { gridColumn: "span 2" } }, TransactionHistory()), ); } function Footer() { return h( "div", { className: "footer" }, h( "div", { className: "wrapper" }, h( "div", null, h("h3", null, "Secure Banking"), h( "ul", { style: { listStyleType: "none", padding: 0 } }, h("li", null, "FDIC Insured"), h("li", null, "Fraud Protection"), h("li", null, "24/7 Support"), h("li", null, "API Access"), ), ), h( "div", null, h("h3", null, "Notices"), h( "ul", { style: { listStyleType: "none", padding: 0 } }, h("li", null, "Terms of Service"), h("li", null, "Privacy Policy"), h("li", null, "Interest Rates"), h("li", null, "Report Fraud"), ), ), ), ); } function App() { return h( "div", { className: "app-shell" }, WindowTitleBar(), h( "main", null, Navbar(), h("div", { className: "container" }, BankDashboard()), Footer(), ), ); } //============================================================================= // #region ARMA 3 INTEGRATION //============================================================================= function sendEvent(event, data) { if (typeof A3API !== "undefined") { A3API.SendAlert(JSON.stringify({ event, data })); } else { console.log("Event:", event, "Data:", data); } } //============================================================================= // #region INITIALIZATION //============================================================================= let initialized = false; function initBank() { if (initialized) return; const root = document.getElementById("app"); if (root) { if (typeof store !== "undefined") { store.subscribe(() => _render()); } render(App, root); initialized = true; console.log("[Bank] Interface initialized"); } } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initBank); } else { initBank(); }