/** * 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);