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