From 21cb0aeb50f49b23a3a195dd402adc91bdd78ca2 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 16 Oct 2026 11:15:28 +0200 Subject: [PATCH] Replace alert/confirm with modals, hide sector numbers on cramped layout, enforce 2/3/6 grid for All Sectors --- frontend/css/layout/responsive.css | 34 ++++++++++++ frontend/css/layout/workspace.css | 24 ++++++++- frontend/index.html | 21 ++++++++ frontend/js/app.js | 86 +++++++++++++++++++++++++----- 4 files changed, 151 insertions(+), 14 deletions(-) diff --git a/frontend/css/layout/responsive.css b/frontend/css/layout/responsive.css index bcfb39b..e58f688 100644 --- a/frontend/css/layout/responsive.css +++ b/frontend/css/layout/responsive.css @@ -66,6 +66,23 @@ } } +/* Cramped / Mobile viewport: Hide sector pill numbers so only icons are displayed */ +@media (max-width: 640px) { + .sector-pill .sector-pill-rate { + display: none !important; + } + + .sector-pill { + padding: 8px 4px; + gap: 0; + } + + .sector-pill iconify-icon { + font-size: 1.25rem; + } +} + + /* ========================================================================== TOASTS & MODALS ========================================================================== */ @@ -232,3 +249,20 @@ .btn-text-close:hover { color: var(--text-primary); } + +.modal-error-box { + background: #fef2f2; + border: 1px solid #fecaca; + color: #b91c1c; + padding: 8px 12px; + border-radius: var(--radius-sm); + font-size: 0.78rem; + font-weight: 600; +} + +.modal-body-text { + font-size: 0.88rem; + color: var(--text-secondary); + line-height: 1.5; +} + diff --git a/frontend/css/layout/workspace.css b/frontend/css/layout/workspace.css index bad2788..9659e86 100644 --- a/frontend/css/layout/workspace.css +++ b/frontend/css/layout/workspace.css @@ -216,13 +216,33 @@ color: var(--emerald-dark); } -/* All Sectors Overview Grid */ +/* All Sectors Overview Grid (Strict 6, 3, or 2 grid for 6 sectors) */ .all-sectors-summary-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + grid-template-columns: repeat(3, 1fr); gap: 12px; } +@media (min-width: 1400px) { + .all-sectors-summary-grid { + grid-template-columns: repeat(6, 1fr); + } +} + +@media (max-width: 768px) { + .all-sectors-summary-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 480px) { + .all-sectors-summary-grid { + grid-template-columns: repeat(2, 1fr); + gap: 8px; + } +} + + .sector-mini-card { background: var(--bg-surface); border: 1px solid var(--border-color); diff --git a/frontend/index.html b/frontend/index.html index 393af59..7eb1d10 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -213,6 +213,9 @@ + + + + + + diff --git a/frontend/js/app.js b/frontend/js/app.js index 9126b8c..d91f100 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -275,15 +275,23 @@ class ProductionApp { } case 'TRIGGER_IPO': { - const confirmed = confirm('Confirm Corporate Initial Public Offering (IPO)?\n\nYour factories and cash will reset in exchange for permanent Venture Shares ($V-Shares) and corporate equity!'); - if (confirmed) { - const success = this.sm.performIPO(); - if (success) { - this.showToast(`IPO Successful! Welcome to Wall Street, CEO!`, 'success'); - this.mountActiveView(); - this.mountInspectorPane(); + this.showConfirmModal({ + title: 'Authorize Initial Public Offering (IPO)', + subtitle: 'Corporate Equity Transition', + icon: 'lucide:trending-up', + text: 'Confirm Corporate Initial Public Offering (IPO)?

All factory floors and cash will be liquidated in exchange for permanent Venture Shares ($V-Shares) and corporate prestige bonuses!', + okText: 'Execute IPO', + cancelText: 'Abort' + }).then(confirmed => { + if (confirmed) { + const success = this.sm.performIPO(); + if (success) { + this.showToast(`IPO Successful! Welcome to Wall Street, CEO!`, 'success'); + this.mountActiveView(); + this.mountInspectorPane(); + } } - } + }); break; } @@ -468,8 +476,52 @@ class ProductionApp { }, 3200); } + showConfirmModal({ title = 'Executive Authorization', subtitle = 'Board Confirmation', icon = 'lucide:alert-triangle', text = '', okText = 'Confirm', cancelText = 'Cancel' } = {}) { + return new Promise((resolve) => { + const modal = document.getElementById('confirm-modal'); + const elTitle = document.getElementById('confirm-modal-title'); + const elSub = document.getElementById('confirm-modal-subtitle'); + const elIcon = document.getElementById('confirm-modal-icon'); + const elText = document.getElementById('confirm-modal-text'); + const btnOk = document.getElementById('btn-confirm-ok'); + const btnCancel = document.getElementById('btn-confirm-cancel'); + + if (!modal) return resolve(false); + + if (elTitle) elTitle.textContent = title; + if (elSub) elSub.textContent = subtitle; + if (elIcon) elIcon.setAttribute('icon', icon); + if (elText) elText.innerHTML = text; + if (btnOk) btnOk.textContent = okText; + if (btnCancel) btnCancel.textContent = cancelText; + + const cleanup = () => { + modal.classList.remove('open'); + btnOk.onclick = null; + btnCancel.onclick = null; + }; + + btnOk.onclick = () => { + cleanup(); + resolve(true); + }; + + btnCancel.onclick = () => { + cleanup(); + resolve(false); + }; + + modal.classList.add('open'); + }); + } + openAuthModal() { const modal = document.getElementById('auth-modal'); + const errBox = document.getElementById('auth-error-msg'); + if (errBox) { + errBox.style.display = 'none'; + errBox.textContent = ''; + } if (modal) modal.classList.add('open'); } @@ -478,10 +530,20 @@ class ProductionApp { if (modal) modal.classList.remove('open'); } + showAuthError(msg) { + const errBox = document.getElementById('auth-error-msg'); + if (errBox) { + errBox.textContent = msg; + errBox.style.display = 'block'; + } else { + this.showToast(msg, 'warning'); + } + } + async handleLogin() { const u = document.getElementById('auth-username')?.value.trim(); const p = document.getElementById('auth-password')?.value; - if (!u || !p) return alert('Please enter username and password'); + if (!u || !p) return this.showAuthError('Please enter username and password'); try { await this.sync.login(u, p); @@ -489,7 +551,7 @@ class ProductionApp { this.closeAuthModal(); this.updateHud(); } catch (err) { - alert(err.message || 'Login failed'); + this.showAuthError(err.message || 'Login failed'); } } @@ -497,7 +559,7 @@ class ProductionApp { const u = document.getElementById('auth-username')?.value.trim(); const p = document.getElementById('auth-password')?.value; const hp = document.getElementById('auth-website-hp')?.value; - if (!u || !p) return alert('Please enter username and password'); + if (!u || !p) return this.showAuthError('Please enter username and password'); try { await this.sync.register(u, p, hp); @@ -505,7 +567,7 @@ class ProductionApp { this.closeAuthModal(); this.updateHud(); } catch (err) { - alert(err.message || 'Registration failed'); + this.showAuthError(err.message || 'Registration failed'); } }