/** * LandveX Finance — Entry Point * Sprint 3: UI-refaktorering * * Initierar appen, laddar state, sätter upp event listeners. */ import state from './state.mjs'; import { getHealth } from './api.mjs'; import { $, on } from './utils.mjs'; // ── Tab-modules (lazy-loaded) ── const tabModules = { overview: () => import('./tabs/overview.mjs'), transactions: () => import('./tabs/transactions.mjs'), invoices: () => import('./tabs/invoices.mjs'), vat: () => import('./tabs/vat.mjs'), payroll: () => import('./tabs/payroll.mjs'), period: () => import('./tabs/period.mjs'), tasks: () => import('./tabs/tasks.mjs'), forecast: () => import('./tabs/forecast.mjs'), }; let currentTabModule = null; // ── Init ── export async function init() { console.log('[finance] init start'); // 1. Bygg period-väljare buildPeriodSelects(); // 2. Sätt upp globala event listeners setupGlobalEvents(); // 3. Ladda initial tab await switchTab(state.get().tab); // 4. Kolla health checkHealth(); // 5. Sätt upp state-subscribers state.subscribe(onStateChange); console.log('[finance] init done'); } // ── Tab-switching ── export async function switchTab(tab) { if (!tabModules[tab]) { console.error('[finance] unknown tab:', tab); return; } // Deaktivera nuvarande tab if (currentTabModule?.deactivate) { currentTabModule.deactivate(); } // Uppdatera UI document.querySelectorAll('.tab-sec').forEach(s => s.classList.remove('active')); document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); const tabEl = $(`tab-${tab}`); if (tabEl) tabEl.classList.add('active'); const tabIndex = { overview: 0, transactions: 1, invoices: 2, vat: 3, payroll: 4, period: 5, tasks: 6, forecast: 7 }; const tabBtns = document.querySelectorAll('.tab-btn'); if (tabBtns[tabIndex[tab] ?? 0]) { tabBtns[tabIndex[tab] ?? 0].classList.add('active'); } // Uppdatera state state.setTab(tab); // Lazy-load modul try { const module = await tabModules[tab](); currentTabModule = module; if (module.activate) { await module.activate(state.get().period); } } catch (e) { console.error('[finance] failed to load tab:', tab, e); showError(`Kunde inte ladda ${tab}`); } } // ── Period ── function buildPeriodSelects() { const FISCAL = state.get().fiscalYear; const ms = $('sel-month'); if (!ms) return; ms.innerHTML = ''; for (let m = 1; m <= 12; m++) { const v = `${FISCAL}-${String(m).padStart(2, '0')}`; const o = document.createElement('option'); o.value = v; o.textContent = `${MN[m - 1]} ${FISCAL}`; if (v === state.get().period) o.selected = true; ms.appendChild(o); } ms.onchange = () => state.setPeriod(ms.value); const qs = $('sel-quarter'); if (qs) { qs.innerHTML = ''; const curM = parseInt((state.get().period || '').split('-')[1] || 6); for (let q = 1; q <= 4; q++) { const o = document.createElement('option'); o.value = `Q${q}-${FISCAL}`; o.textContent = `Q${q} ${['Jan–Mar', 'Apr–Jun', 'Jul–Sep', 'Okt–Dec'][q - 1]} ${FISCAL}`; if (Math.ceil(curM / 3) === q) o.selected = true; qs.appendChild(o); } qs.onchange = () => state.setPeriod(qs.value); } } // ── Globala events ── function setupGlobalEvents() { // Escape stänger modaler on(document, 'keydown', (e) => { if (e.key === 'Escape') { closeAllModals(); } }); // Window resize (debounced) let resizeTimer; on(window, 'resize', () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { state.set({ windowWidth: window.innerWidth }); }, 250); }); } // ── State change handler ── function onStateChange(s) { // Uppdatera loading-indikatorer const isLoading = s.loading.size > 0; const statusEl = $('data-status'); if (statusEl) { statusEl.innerHTML = isLoading ? 'Laddar…' : 'Live'; } } // ── Health check ── async function checkHealth() { try { await getHealth(); console.log('[finance] health: ok'); } catch (e) { console.warn('[finance] health check failed:', e.message); } } // ── Modaler ── export function openModal(id) { const el = $(id); if (el) el.classList.remove('hidden'); } export function closeModal(id) { const el = $(id); if (el) el.classList.add('hidden'); } export function closeAllModals() { document.querySelectorAll('.modal-bg').forEach(m => m.classList.add('hidden')); } // ── Felhantering ── export function showError(msg) { console.error('[finance]', msg); // TODO: Toast/notifikation alert(msg); // Fallback } // ── Start ── if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }