forge/arma/ui/bank.js
Jacob Schmidt d178e39164 Refactor client UI stores and normalize docs formatting
- Rework org and store UI state modules (rename/move store/getter files, add runtime and bridge wiring)
- Update store UI components and page structure (navbar/cart split, new StoreView flow)
- Apply broad markdown/YAML/HTML/CSS/JS formatting cleanup across docs, templates, and workflows
2026-03-10 19:13:30 -05:00

442 lines
13 KiB
JavaScript

/**
* Player Bank App - Vanilla JS "React-like" Implementation
*/
// --- 1. The "Library" Logic (Reused) ---
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 {
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) => el.appendChild(c));
}
});
return el;
}
let _rootContainer = null;
let _rootComponent = null;
function render(component, container) {
_rootContainer = container;
_rootComponent = component;
_render();
}
function _render() {
_rootContainer.innerHTML = "";
_rootContainer.appendChild(_rootComponent());
}
const createSignal = (initialValue) => {
let _val = initialValue;
const getValue = () => _val;
const setValue = (newValue) => {
_val = typeof newValue === "function" ? newValue(_val) : newValue;
_render();
};
return [getValue, setValue];
};
// --- 2. Bank Application Components ---
// Global State
const [getView, setView] = createSignal("login"); // 'login', 'dashboard'
const [getBalance, setBalance] = createSignal(1250000);
const [getPending, setPending] = createSignal(45250); // Mock pending earnings
const [getTransactions, setTransactions] = createSignal([
{
id: 1,
type: "credit",
desc: "Contract Payment: OP-442",
amount: 150000,
date: "2026-02-05",
},
{
id: 2,
type: "debit",
desc: "Equipment Purchase: Ammunition",
amount: -4500,
date: "2026-02-04",
},
{
id: 3,
type: "debit",
desc: "Vehicle Maintenance",
amount: -1200,
date: "2026-02-03",
},
]);
// Header
function Header() {
return h(
"div",
{ className: "header" },
h(
"h1",
{
style: { cursor: "pointer" },
onClick: () => setView("login"),
},
"Global Financial Network",
),
h("p", null, "Secure Banking"),
);
}
// Login View
function BankLogin() {
const handleSubmit = (e) => {
e.preventDefault();
setView("dashboard");
};
return h(
"div",
{ className: "card", style: { maxWidth: "400px", margin: "0 auto" } },
h("h2", null, "Secure Access"),
h(
"form",
{ onSubmit: handleSubmit },
h(
"div",
null,
h("label", null, "Account ID"),
h("input", { type: "text", placeholder: "xxxx-xxxx-xxxx" }),
),
h(
"div",
null,
h("label", null, "Security PIN"),
h("input", { type: "password", placeholder: "••••" }),
),
h(
"div",
{ className: "form-actions" },
h(
"button",
{ type: "submit", style: { width: "100%" } },
"Authenticate",
),
h(
"p",
{
style: {
fontSize: "0.8rem",
color: "var(--text-muted)",
marginTop: "1rem",
},
},
"Authorized Personnel Only",
),
),
),
);
}
// Transaction History Helper
function TransactionHistory() {
const transactions = getTransactions();
return h(
"div",
{ className: "card" },
h(
"h3",
{
style: {
textAlign: "left",
borderBottom: "1px solid var(--border)",
paddingBottom: "1rem",
marginBottom: "1rem",
},
},
"Recent Transactions",
),
h(
"ul",
{ style: { listStyle: "none", padding: 0 } },
...transactions.map((tx) =>
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.desc),
h(
"div",
{
style: {
fontSize: "0.85rem",
color: "var(--text-muted)",
},
},
tx.date,
),
),
h(
"div",
{
style: {
fontWeight: "700",
color:
tx.type === "credit"
? "#10b981"
: "#ef4444",
},
},
(tx.type === "credit" ? "+" : "") +
"$" +
Math.abs(tx.amount).toLocaleString(),
),
),
),
),
);
}
// Transfer Form
function TransferForm() {
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const amount = parseFloat(formData.get("amount"));
if (amount > 0 && amount <= getBalance()) {
setBalance((prev) => prev - amount);
const newTx = {
id: Date.now(),
type: "debit",
desc: "Transfer to " + formData.get("recipient"),
amount: -amount,
date: new Date().toISOString().split("T")[0],
};
setTransactions((prev) => [newTx, ...prev]);
}
};
return h(
"div",
{ className: "card" },
h("h2", null, "Wire Transfer"),
h(
"form",
{ onSubmit: handleSubmit },
h(
"div",
null,
h("label", null, "Recipient Name / GUID"),
h("input", {
name: "recipient",
type: "text",
placeholder: "Enter Name or GUID",
}),
),
h(
"div",
null,
h("label", null, "Amount"),
h("input", {
name: "amount",
type: "number",
placeholder: "0.00",
}),
),
h("button", { type: "submit" }, "Send Funds"),
),
);
}
// Dashboard View
function BankDashboard() {
return h(
"div",
{ className: "content" },
// Top Row: Balance
h(
"div",
{ className: "card", style: { gridColumn: "span 2" } },
h(
"h2",
{
style: {
fontSize: "1.2rem",
color: "var(--text-muted)",
textTransform: "uppercase",
letterSpacing: "0.05em",
},
},
"Total Balance",
),
h(
"div",
{
style: {
fontSize: "2.8rem",
fontWeight: "800",
color: "var(--primary-hover)",
margin: "1rem 0",
},
},
"$" + getBalance().toLocaleString(),
),
h(
"div",
{
style: {
textAlign: "center",
marginBottom: "1.5rem",
color: "var(--text-muted)",
fontSize: "1.2rem",
},
},
"Pending: ",
h(
"span",
{ style: { color: "#fbbf24", fontWeight: "bold" } },
"$" + getPending().toLocaleString(),
),
),
h(
"div",
{
style: {
display: "flex",
gap: "1rem",
justifyContent: "center",
},
},
h(
"button",
{
onClick: () => {
const pending = getPending();
if (pending > 0) {
setBalance((prev) => prev + pending);
setPending(0);
const newTx = {
id: Date.now(),
type: "credit",
desc: "Field Deposit",
amount: pending,
date: new Date()
.toISOString()
.split("T")[0],
};
setTransactions((prev) => [newTx, ...prev]);
}
},
style: {
opacity: getPending() > 0 ? "1" : "0.5",
cursor: getPending() > 0 ? "pointer" : "default",
},
},
"Deposit Pending",
),
h(
"button",
{
style: {
background: "var(--bg-surface-hover)",
color: "var(--text-main)",
border: "1px solid var(--border)",
},
},
"Statement",
),
),
),
// Middle Row: Transfer Form
TransferForm(),
// Bottom Row: History (Full Width in simplified grid, or separate)
TransactionHistory(),
);
}
// Footer
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"),
),
),
),
);
}
// Main App
function App() {
const view = getView();
let mainContent;
if (view === "login") {
mainContent = BankLogin();
} else if (view === "dashboard") {
mainContent = BankDashboard();
}
return h(
"main",
null,
h("div", { className: "container" }, Header(), mainContent),
Footer(),
);
}
// Mount
const root = document.getElementById("app");
render(App, root);