(chore) wip of new ui
This commit is contained in:
parent
096418cc78
commit
abde6649ac
235
arma/ui/app.js
Normal file
235
arma/ui/app.js
Normal file
@ -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);
|
||||||
26
arma/ui/atm.html
Normal file
26
arma/ui/atm.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>ATM - Global Financial Network</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="atm.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
272
arma/ui/atm.js
Normal file
272
arma/ui/atm.js
Normal file
@ -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);
|
||||||
16
arma/ui/bank.html
Normal file
16
arma/ui/bank.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FDIC - Global Financial Network</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="bank.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
265
arma/ui/bank.js
Normal file
265
arma/ui/bank.js
Normal file
@ -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);
|
||||||
16
arma/ui/index.html
Normal file
16
arma/ui/index.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>ORBIS - Global Organization Network</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
303
arma/ui/style.css
Normal file
303
arma/ui/style.css
Normal file
@ -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);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user