diff --git a/arma/ui/app.js b/arma/ui/app.js new file mode 100644 index 0000000..02a2219 --- /dev/null +++ b/arma/ui/app.js @@ -0,0 +1,235 @@ +/** + * Simple React-like Vanilla JS Implementation + */ + +// --- 1. The "Library" Logic --- + +// Helper to create DOM elements (like React.createElement) +function h(tag, props = {}, ...children) { + const el = document.createElement(tag); + + // Handle props + 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); + } + }); + } + + // Handle children + 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; +} + +// Simple Rendering Logic +let _rootContainer = null; +let _rootComponent = null; + +function render(component, container) { + _rootContainer = container; + _rootComponent = component; + _render(); +} + +function _render() { + _rootContainer.innerHTML = ''; // Clear previous tree (simple re-render) + _rootContainer.appendChild(_rootComponent()); +} + +// Simple State Hook (Global for simplicity, or localized using closures) +// Note: In a real app this would be more complex to handle multiple components. +// For this demo, we'll re-render the whole app on state change. +const createSignal = (initialValue) => { + let _val = initialValue; + const getValue = () => _val; + const setValue = (newValue) => { + _val = typeof newValue === 'function' ? newValue(_val) : newValue; + _render(); // Trigger re-render + }; + return [getValue, setValue]; +}; + +// --- 2. The Application Components --- + +// Global View State: 'home', 'login', 'create' +const [getView, setView] = createSignal('home'); + +// Header Component +function Header({ title }) { + return h('div', { className: 'header' }, + h('h1', { + style: { cursor: 'pointer' }, + onClick: () => setView('home') + }, title), + h('p', null, 'Organization Registration & Management Portal') + ); +} + +// Login Form Component +function LoginForm() { + const handleSubmit = (e) => { + e.preventDefault(); // Critical for strict sandbox + const formData = new FormData(e.target); + const data = Object.fromEntries(formData.entries()); + console.log('Login Attempt:', data); + // TODO: Handle authentication logic here + }; + + return h('div', { className: 'card', style: { maxWidth: '400px', margin: '0 auto' } }, + h('h2', null, 'Organization Login'), + h('form', { onSubmit: handleSubmit }, + h('div', null, + h('label', null, 'Email'), + h('input', { name: 'email', type: 'text', placeholder: 'admin@spearnet.mil' }) + ), + h('div', null, + h('label', null, 'Password'), + h('input', { name: 'password', type: 'password', placeholder: '••••••••' }) + ), + h('div', { className: 'form-actions' }, + h('button', { type: 'submit', style: { width: '100%' } }, 'Access Authenticator'), + h('span', { + className: 'cancel-link', + onClick: () => setView('home') + }, 'Cancel / Return to Main') + ) + ) + ); +} + +// Create Org Form Component +function CreateOrgForm() { + const handleSubmit = (e) => { + e.preventDefault(); // Critical for strict sandbox + const formData = new FormData(e.target); + const data = Object.fromEntries(formData.entries()); + console.log('Org Registration:', data); + // TODO: Handle registration logic here + }; + + return h('div', { className: 'split-container' }, + h('div', { className: 'info-panel' }, + h('h2', null, 'Registration Details'), + h('p', null, 'Complete the form to add your organization to the Global Organization Registry.'), + h('ul', { style: { textAlign: 'left', marginTop: '1.5rem', listStyleType: 'none', padding: 0 } }, + h('li', { style: { marginBottom: '0.5rem' } }, '✅ Official Organization Designator'), + h('li', { style: { marginBottom: '0.5rem' } }, '✅ Secure Comms Channel'), + h('li', { style: { marginBottom: '0.5rem' } }, '✅ Deployment Roster Access'), + h('li', { style: { marginBottom: '0.5rem' } }, '✅ After-Action Report Tools') + ), + h('div', { className: 'price-tag', style: { marginTop: '2rem', padding: '1rem', background: 'var(--bg-app)', borderRadius: 'var(--radius)', border: '1px solid var(--border)' } }, + h('span', { style: { display: 'block', fontSize: '0.9rem', color: 'var(--text-muted)' } }, 'Registration Fee'), + h('span', { style: { display: 'block', fontSize: '2rem', fontWeight: '700', color: 'var(--primary)' } }, '$50,000') + ) + ), + h('div', { className: 'form-panel card', style: { margin: 0 } }, + h('h2', null, 'Organization Registration'), + h('form', { onSubmit: handleSubmit }, + h('div', null, + h('label', null, 'Organization Name'), + h('input', { name: 'orgName', type: 'text', placeholder: 'e.g. Task Force 141' }) + ), + h('div', null, + h('label', null, 'Organization Type'), + h('select', { name: 'type' }, + h('option', { value: 'infantry' }, 'Infantry / Milsim'), + h('option', { value: 'aviation' }, 'Aviation Wing'), + h('option', { value: 'pmc' }, 'Private Military Company'), + h('option', { value: 'support' }, 'Logistics & Support') + ) + ), + h('div', { className: 'form-actions' }, + h('button', { type: 'submit', style: { width: '100%' } }, 'Submit Registration'), + h('span', { + className: 'cancel-link', + onClick: () => setView('home') + }, 'Cancel / Return to Main') + ) + ) + ) + ); +} + +// Home View Component +function HomeView() { + return h('div', { className: 'content' }, + h('div', { className: 'card' }, + h('h2', null, 'Create Organization'), + h('p', null, 'Establish your Task Force, PMC, or Milsim unit with the Global Organization Network. Receive your official unit designator and TO&E authorization instantly.'), + h('button', { onClick: () => setView('create') }, 'Register') + ), + h('div', { className: 'card' }, + h('h2', null, 'Organization Dashboard'), + h('p', null, 'Access your unit dashboard to modify rosters, adjust active deployments, and submit after-action reports through the secure field uplink.'), + h('button', { onClick: () => setView('login') }, 'Login') + ) + ); +} + +// Footer Component (unchanged) +function Footer() { + return h('div', { className: 'footer' }, + h('div', { className: 'wrapper' }, + h('div', null, + h('h3', null, 'Registry Resources'), + h('ul', { style: { listStyleType: 'none', padding: 0 } }, + h('li', null, 'Registration Guidelines'), + h('li', null, 'Tax & Fee Schedule'), + h('li', null, 'Legal Compliance'), + h('li', null, 'Trademark Database') + ) + ), + h('div', null, + h('h3', null, 'Bureau Support'), + h('ul', { style: { listStyleType: 'none', padding: 0 } }, + h('li', null, 'Office: Sector 7 Admin Block'), + h('li', null, 'Hours: 0800 - 1600 (GST)'), + h('li', null, 'Helpdesk: 555-01-REGISTRY'), + h('li', null, 'support@org-bureau.gov') + ) + ) + ) + ); +} + +// Main App Component +function App() { + const view = getView(); + + let mainContent; + if (view === 'home') { + mainContent = HomeView(); + } else if (view === 'login') { + mainContent = LoginForm(); + } else if (view === 'create') { + mainContent = CreateOrgForm(); + } + + return h('main', null, + h('div', { className: 'container' }, + Header({ title: 'Global Organization Network' }), + mainContent + ), + Footer() + ); +} + +// --- 3. Mount Application --- +const root = document.getElementById('app'); +render(App, root); diff --git a/arma/ui/atm.html b/arma/ui/atm.html new file mode 100644 index 0000000..62a23d6 --- /dev/null +++ b/arma/ui/atm.html @@ -0,0 +1,26 @@ + + + + + + + ATM - Global Financial Network + + + + + +
+ + + + diff --git a/arma/ui/atm.js b/arma/ui/atm.js new file mode 100644 index 0000000..a054ad4 --- /dev/null +++ b/arma/ui/atm.js @@ -0,0 +1,272 @@ +/** + * ATM App - Vanilla JS Kiosk 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. ATM Application Components --- + +// Global State +const [getView, setView] = createSignal('pin'); // 'pin', 'menu', 'withdraw', 'custom_withdraw', 'balance' +const [getPin, setPin] = createSignal(''); +const [getCustomAmount, setCustomAmount] = createSignal(''); // For custom withdrawal +const [getBalance, setBalance] = createSignal(1250000); // Shared mockup balance +const [getMessage, setMessage] = createSignal(''); // For feedback + +// Header +function Header() { + return h('div', { className: 'header', style: { marginBottom: '2rem' } }, + h('h1', null, 'ATM TERMINAL'), + h('p', null, 'Global Financial Network') + ); +} + +// PIN Entry View +function PinView() { + const currentPin = getPin(); + + const handleNumClick = (num) => { + if (currentPin.length < 4) { + setPin(prev => prev + num); + } + }; + + const handleClear = () => setPin(''); + const handleEnter = () => { + if (currentPin.length === 4) { + // Mock auth success + setView('menu'); + } else { + setMessage('Invalid PIN Length'); + setTimeout(() => setMessage(''), 2000); + } + }; + + return h('div', { className: 'card', style: { padding: '3rem 2rem' } }, + h('h2', null, 'Enter Security PIN'), + h('div', { className: 'pin-display' }, + currentPin.replace(/./g, '•') || '----' + ), + h('p', { style: { color: 'red', height: '1.5rem' } }, getMessage()), + h('div', { className: 'numpad' }, + ['1', '2', '3', '4', '5', '6', '7', '8', '9'].map(num => + h('button', { onClick: () => handleNumClick(num) }, num) + ), + h('button', { style: { background: '#ef4444', color: 'white' }, onClick: handleClear }, 'C'), + h('button', { onClick: () => handleNumClick('0') }, '0'), + h('button', { style: { background: '#10b981', color: 'white' }, onClick: handleEnter }, '↵') + ) + ); +} + +// Main Menu View +function MenuView() { + return h('div', { className: 'kiosk-content' }, + h('h2', { style: { textAlign: 'center', marginBottom: '1rem' } }, 'Select Transaction'), + h('div', { className: 'kiosk-menu-stack' }, + h('button', { className: 'kiosk-btn', onClick: () => setView('withdraw') }, + 'Withdraw Cash' + ), + h('button', { className: 'kiosk-btn', onClick: () => setView('balance') }, + 'Check Balance' + ), + h('button', { + className: 'kiosk-btn', + style: { background: 'var(--bg-surface)', color: 'var(--text-main)', border: '1px solid var(--border)' }, + onClick: () => { + setPin(''); + setView('pin'); + } + }, 'Cancel Transaction') + ) + ); +} + +// Withdraw View +function WithdrawView() { + const handleWithdraw = (amount) => { + if (getBalance() >= amount) { + setBalance(prev => prev - amount); + setMessage(`Please take your cash: $${amount}`); + setTimeout(() => { + setMessage(''); + setView('menu'); + }, 3000); + } else { + setMessage('Insufficient Funds'); + setTimeout(() => setMessage(''), 2000); + } + }; + + if (getMessage()) { + return h('div', { className: 'card', style: { padding: '4rem', textAlign: 'center' } }, + h('h2', { style: { color: 'var(--primary)' } }, getMessage()) + ); + } + + return h('div', { className: 'kiosk-content' }, + h('h2', { style: { textAlign: 'center', marginBottom: '1rem' } }, 'Select Amount'), + h('div', { className: 'kiosk-grid' }, + h('button', { className: 'kiosk-btn', onClick: () => handleWithdraw(20) }, '$20'), + h('button', { className: 'kiosk-btn', onClick: () => handleWithdraw(50) }, '$50'), + h('button', { className: 'kiosk-btn', onClick: () => handleWithdraw(100) }, '$100'), + h('button', { + className: 'kiosk-btn', + onClick: () => { + setCustomAmount(''); + setView('custom_withdraw'); + } + }, 'Other Amount'), + h('button', { className: 'kiosk-btn', style: { gridColumn: 'span 2', background: 'var(--text-muted)' }, onClick: () => setView('menu') }, 'Cancel') + ) + ); +} + +// Custom Withdraw View +function CustomWithdrawView() { + const currentAmount = getCustomAmount(); + + const handleNumClick = (num) => { + if (currentAmount.length < 5) { // Limit to 5 digits for safety + setCustomAmount(prev => prev + num); + } + }; + + const handleClear = () => setCustomAmount(''); + + const handleEnter = () => { + const amount = parseInt(currentAmount, 10); + if (amount > 0) { + if (getBalance() >= amount) { + setBalance(prev => prev - amount); + setMessage(`Please take your cash: $${amount}`); + setTimeout(() => { + setMessage(''); + setView('menu'); + }, 3000); + } else { + setMessage('Insufficient Funds'); + setTimeout(() => setMessage(''), 2000); + } + } else { + setMessage('Invalid Amount'); + setTimeout(() => setMessage(''), 2000); + } + }; + + if (getMessage()) { + return h('div', { className: 'card', style: { padding: '4rem', textAlign: 'center' } }, + h('h2', { style: { color: 'var(--primary)' } }, getMessage()) + ); + } + + return h('div', { className: 'card', style: { padding: '3rem 2rem' } }, + h('h2', null, 'Enter Amount'), + h('div', { className: 'pin-display' }, + currentAmount ? `$${currentAmount}` : '$0' + ), + h('div', { className: 'numpad' }, + ['1', '2', '3', '4', '5', '6', '7', '8', '9'].map(num => + h('button', { onClick: () => handleNumClick(num) }, num) + ), + h('button', { style: { background: '#ef4444', color: 'white' }, onClick: handleClear }, 'C'), + h('button', { onClick: () => handleNumClick('0') }, '0'), + h('button', { style: { background: '#10b981', color: 'white' }, onClick: handleEnter }, '↵') + ), + h('button', { + style: { width: '100%', marginTop: '2rem', padding: '1rem', background: 'var(--text-muted)' }, + onClick: () => setView('withdraw') + }, 'Cancel') + ); +} + +// Balance View +function BalanceView() { + return h('div', { className: 'card', style: { textAlign: 'center', padding: '3rem' } }, + h('h2', { style: { color: 'var(--text-muted)' } }, 'Available Balance'), + h('div', { style: { fontSize: '4rem', fontWeight: '800', margin: '2rem 0', color: 'var(--primary-hover)' } }, + '$' + getBalance().toLocaleString() + ), + h('button', { className: 'kiosk-btn', style: { width: '100%', maxWidth: '300px', margin: '0 auto' }, onClick: () => setView('menu') }, 'Return to Menu') + ); +} + +// Main App +function App() { + const view = getView(); + + let mainContent; + if (view === 'pin') { + mainContent = PinView(); + } else if (view === 'menu') { + mainContent = MenuView(); + } else if (view === 'withdraw') { + mainContent = WithdrawView(); + } else if (view === 'custom_withdraw') { + mainContent = CustomWithdrawView(); + } else if (view === 'balance') { + mainContent = BalanceView(); + } + + return h('main', null, + h('div', { className: 'container' }, + Header(), + mainContent + ) + ); +} + +// Mount +const root = document.getElementById('app'); +render(App, root); diff --git a/arma/ui/bank.html b/arma/ui/bank.html new file mode 100644 index 0000000..fe92a70 --- /dev/null +++ b/arma/ui/bank.html @@ -0,0 +1,16 @@ + + + + + + + FDIC - Global Financial Network + + + + +
+ + + + diff --git a/arma/ui/bank.js b/arma/ui/bank.js new file mode 100644 index 0000000..8fe0486 --- /dev/null +++ b/arma/ui/bank.js @@ -0,0 +1,265 @@ +/** + * 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); diff --git a/arma/ui/index.html b/arma/ui/index.html new file mode 100644 index 0000000..dc62f47 --- /dev/null +++ b/arma/ui/index.html @@ -0,0 +1,16 @@ + + + + + + + ORBIS - Global Organization Network + + + + +
+ + + + diff --git a/arma/ui/style.css b/arma/ui/style.css new file mode 100644 index 0000000..fe13bdd --- /dev/null +++ b/arma/ui/style.css @@ -0,0 +1,303 @@ +:root { + --bg-app: #fdfcf8; + /* Warm white */ + --bg-surface: #ffffff; + --bg-surface-hover: #f1f5f9; + --primary: #475569; + /* Slate gray */ + --primary-hover: #1e293b; + --text-main: #1f2937; + --text-muted: #64748b; + --text-inverse: #f8fafc; + --border: #e2e8f0; + --radius: 8px; + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --footer-bg: #1e293b; + /* Dark slate for footer */ +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + margin: 0; + padding: 0; + background: var(--bg-app); + color: var(--text-main); + line-height: 1.6; +} + +#app { + min-height: 100vh; +} + +main { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +.container { + max-width: 1200px; + width: 100%; + margin: 0 auto; + padding: 2rem; + flex: 1; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +/* Header */ +.header { + text-align: center; + margin-bottom: 3rem; + padding-bottom: 2rem; + border-bottom: 1px solid var(--border); + + h1 { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 0.5rem; + letter-spacing: -0.025em; + color: var(--primary-hover); + } + + p { + color: var(--text-muted); + font-size: 1.1rem; + } +} + +.content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + margin-bottom: 2rem; +} + +/* Cards */ +.card { + background: var(--bg-surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 2rem; + margin-bottom: 2rem; + box-shadow: var(--shadow); + text-align: center; + + h2 { + margin-top: 0; + font-size: 1.8rem; + color: var(--primary-hover); + } +} + +/* Buttons */ +button { + background: var(--primary); + color: white; + border: none; + padding: 0.75rem 1.5rem; + border-radius: var(--radius); + cursor: pointer; + font-size: 1rem; + font-weight: 500; + transition: all 0.2s ease; + + &:hover { + background: var(--primary-hover); + transform: translateY(-1px); + box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); + } + + &+& { + margin-left: 1rem; + } +} + +/* Split Layout */ +.split-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + align-items: center; + width: 100%; +} + +.info-panel { + text-align: left; + padding: 1rem; +} + +/* Forms */ +form { + display: flex; + flex-direction: column; + gap: 1rem; + text-align: left; + + label { + display: block; + margin-bottom: 0.5rem; + color: var(--text-muted); + font-weight: 500; + font-size: 0.9rem; + } + + input, + select { + width: 100%; + padding: 0.75rem; + border-radius: var(--radius); + border: 1px solid var(--border); + background: var(--bg-app); + color: var(--text-main); + font-family: inherit; + font-size: 1rem; + box-sizing: border-box; + transition: border-color 0.2s; + + &:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1); + } + } + + .form-actions { + margin-top: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + } + + .cancel-link { + font-size: 0.9rem; + color: var(--text-muted); + cursor: pointer; + text-decoration: underline; + + &:hover { + color: var(--primary); + } + } +} + +/* Footer */ +.footer { + margin-top: auto; + background: var(--footer-bg); + color: var(--text-inverse); + display: block; + + .wrapper { + max-width: 1200px; + width: 100%; + margin: 0 auto; + padding: 3rem 2rem; + box-sizing: border-box; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + } + + h3 { + color: var(--text-inverse); + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.1em; + font-weight: 700; + margin-bottom: 1.5rem; + border-bottom: 1px solid #475569 !important; + padding-bottom: 0.5rem; + margin-right: 1rem; + } + + ul { + li { + color: #cbd5e1; + font-size: 0.95rem; + margin-bottom: 0.75rem !important; + cursor: pointer; + transition: color 0.2s; + + &:hover { + color: white; + } + } + } +} + +/* ATM Kiosk Styles */ +.kiosk-content { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.kiosk-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; + margin-top: 2rem; + width: 100%; + max-width: 600px; + /* Constrain width for better look */ +} + +.kiosk-menu-stack { + display: flex; + flex-direction: column; + gap: 1.5rem; + margin-top: 2rem; + width: 100%; + max-width: 600px; + /* Narrower for vertical stack */ +} + +.kiosk-btn { + padding: 2rem; + font-size: 1.25rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.5rem; + height: 100%; + min-height: 120px; + margin: 0; +} + +.numpad { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + max-width: 300px; + margin: 0 auto; + + button { + padding: 1.5rem; + font-size: 1.5rem; + background: var(--bg-surface); + color: var(--text-main); + border: 1px solid var(--border); + box-shadow: var(--shadow); + margin: 0; + + &:hover { + background: var(--primary); + color: white; + border-color: var(--primary); + } + } +} + +.pin-display { + font-size: 2.5rem; + letter-spacing: 0.5rem; + text-align: center; + margin-bottom: 2rem; + font-family: monospace; + color: var(--primary); +}