/* Účtovníček — kostra appky: prihlásenie, výber režimu a firmy, bočné menu * podľa režimu, obsah vpravo. Adresa: #rezim/firma/stranka[:akcia:id]. */ function parseHash() { const h = (location.hash || '').slice(1); const parts = h.split('/'); if (parts.length >= 3) return { mode: parts[0], firma: parts[1], route: parts.slice(2).join('/') || 'dashboard' }; return { mode: '', firma: '', route: h || 'dashboard' }; // stará adresa bez režimu } function App({ onLogout }) { const [, setTick] = React.useState(0); const rerender = () => setTick(v => v + 1); const [route, setRoute] = React.useState(() => parseHash().route); const [busy, setBusy] = React.useState(false); const [setToast, Toast] = useToast(); React.useEffect(() => { const onHash = async () => { const p = parseHash(); if (p.mode && uctStore.ctx.resolve(p.mode, p.firma)) { setBusy(true); try { await uctStore.__hydrate(); } catch (e) { if (!(e instanceof AuthError)) setToast(e.message); } setBusy(false); } setRoute(p.route); }; window.addEventListener('hashchange', onHash); return () => window.removeEventListener('hashchange', onHash); }, []); React.useEffect(() => { window.addEventListener('uct:changed', rerender); return () => window.removeEventListener('uct:changed', rerender); }, []); const mode = uctStore.ctx.mode(); const firma = uctStore.ctx.firma(); const go = (r) => { const h = '#' + mode + '/' + firma + '/' + r; if (location.hash !== h) history.replaceState(null, '', h); setRoute(r); }; const switchTo = async (m, f) => { setBusy(true); try { await uctStore.ctx.set(m, f); const r = 'dashboard'; history.replaceState(null, '', '#' + uctStore.ctx.mode() + '/' + uctStore.ctx.firma() + '/' + r); setRoute(r); } catch (e) { if (!(e instanceof AuthError)) setToast(e.message); } setBusy(false); }; const top = route.split(':')[0]; const company = uctStore.company.get(); const settings = uctStore.settings.get(); const unpaid = (kind) => uctStore.invoices.list(kind).filter(i => i.status === 'vystavena' || i.status === 'ciastocne').length; const modes = uctStore.ctx.modes(); const firms = uctStore.ctx.companiesFor(mode); const user = uctStore.auth.user(); const modeLabel = (uctStore.MODES[mode] || {}).label || ''; const logout = async () => { await uctStore.auth.logout(); onLogout(); }; return (
{modeLabel} › {company.name || firma} › {labelFor(top)}
{fmt.dateLong(new Date())} · {user ? (user.name || user.login) : ''}
{busy &&
Načítavam dáta firmy…
} {settings.demoDataPresent && (
V appke sú ukážkové (vymyslené) dáta, aby bolo vidno, ako vyzerá. Keď začneš účtovať naozaj, vymaž ich.
)} {top === 'dashboard' && } {top === 'invoices' && } {top === 'proforma' && } {top === 'purchase' && } {top === 'bank' && } {top === 'cash' && } {top === 'internal' && } {top === 'journal' && } {top === 'ledger' && } {top === 'saldo' && } {top === 'accounts' && } {top === 'receivables' && } {top === 'incomes' && } {top === 'taxcalc' && } {top === 'partners' && } {top === 'settings' && }
{Toast}
); } function NavBtn({ icon, label, active, onClick, count, soon }) { return ( ); } function labelFor(top) { return ({ dashboard: 'Nástenka', invoices: 'Vystavené faktúry', proforma: 'Zálohové faktúry', purchase: 'Prijaté faktúry', bank: 'Banka', cash: 'Pokladňa', internal: 'Interné doklady', journal: 'Účtovný denník', ledger: 'Hlavná kniha', saldo: 'Saldokonto', accounts: 'Účtová osnova', partners: 'Partneri', settings: 'Nastavenia', receivables: 'Pohľadávky', incomes: 'Príjmy', taxcalc: 'Výpočet dane', })[top] || top; } /* --- Prihlásenie / prvé nastavenie účtu --- */ function LoginScreen({ state, onDone }) { const setupNeeded = !!state.setupNeeded; const [login, setLogin] = React.useState(''); const [name, setName] = React.useState(''); const [pw, setPw] = React.useState(''); const [pw2, setPw2] = React.useState(''); const [err, setErr] = React.useState(null); const [busy, setBusy] = React.useState(false); const submit = async (e) => { e.preventDefault(); setErr(null); if (setupNeeded && pw !== pw2) { setErr('Heslá sa nezhodujú.'); return; } setBusy(true); try { if (setupNeeded) await uctStore.auth.setup(login, name, pw); else await uctStore.auth.login(login, pw); onDone(); } catch (e2) { setErr(e2.message); } setBusy(false); }; const provider = state.provider || 'Authentik'; return (
Účtovníček
{state.oidc ? ( <>
Prihlás sa firemným účtom cez {provider}. Dáta sú na serveri Účtovníčka, rovnaké v PC aj v telefóne.
Prihlásiť sa cez {provider} ) : ( <>
{setupNeeded ? 'Prvé spustenie bez servera: vytvor si miestny účet.' : 'Skúšobný režim bez Authentiku: miestny účet a heslo.'}
{err &&
{err}
} setLogin(e.target.value)} placeholder="mirka" /> {setupNeeded && setName(e.target.value)} placeholder="Mirka" />} setPw(e.target.value)} /> {setupNeeded && setPw2(e.target.value)} />}
)}
); } /* --- Štart: stav prihlásenia -> login alebo appka --- */ function Root() { const [phase, setPhase] = React.useState('loading'); // loading | login | app | error const [authState, setAuthState] = React.useState({}); const [error, setError] = React.useState(''); const start = async () => { try { const st = await uctStore.auth.load(); if (!st.user) { setAuthState(st); setPhase('login'); return; } const p = parseHash(); uctStore.ctx.resolve(p.mode, p.firma); if (!uctStore.ctx.mode()) { setError('Používateľ nemá priradenú žiadnu firmu.'); setPhase('error'); return; } const r = p.mode ? p.route : 'dashboard'; history.replaceState(null, '', '#' + uctStore.ctx.mode() + '/' + uctStore.ctx.firma() + '/' + r); await uctStore.__hydrate(); setPhase('app'); } catch (e) { if (e instanceof AuthError) { setAuthState(uctStore.auth.state() || {}); setPhase('login'); return; } setError(e.message || String(e)); setPhase('error'); } }; React.useEffect(() => { start(); }, []); React.useEffect(() => { const onLogout = () => setPhase('login'); window.addEventListener('uct:logout', onLogout); return () => window.removeEventListener('uct:logout', onLogout); }, []); if (phase === 'loading') return
Načítavam dáta…
; if (phase === 'error') return

Účtovníček sa nepodarilo načítať

{error}

Skús okno zavrieť a Účtovníček spustiť znova.

; if (phase === 'login') return { setPhase('loading'); start(); }} />; return setPhase('login')} />; } const __root = document.getElementById('root'); ReactDOM.createRoot(__root).render();