import { useState, useEffect } from 'react'
import { BrowserRouter, Routes, Route, NavLink, Navigate } from 'react-router-dom'
import { api, setCredentials, clearCredentials } from './api'
import Login from './pages/Login'
import Sources from './pages/Sources'
import Import from './pages/Import'
import Rules from './pages/Rules'
import Mappings from './pages/Mappings'
import Records from './pages/Records'
import Log from './pages/Log'
import Pivot from './pages/Pivot'
import Remap from './pages/Remap'
import Stacks from './pages/Stacks'
const NAV = [
{ to: '/sources', label: 'Sources' },
{ to: '/import', label: 'Import' },
{ to: '/rules', label: 'Rules' },
{ to: '/mappings', label: 'Mappings' },
{ to: '/remap', label: 'Remap' },
{ to: '/records', label: 'Records' },
{ to: '/pivot', label: 'Pivot' },
{ to: '/stacks', label: 'Stacks' },
{ to: '/log', label: 'Log' },
]
export default function App() {
const [authed, setAuthed] = useState(false)
const [loginUser, setLoginUser] = useState('')
const [sources, setSources] = useState([])
const [source, setSource] = useState(() => localStorage.getItem('selectedSource') || '')
const [sidebarOpen, setSidebarOpen] = useState(false)
// Sets of names whose dfv view is out of sync with current definitions
const [staleSources, setStaleSources] = useState(new Set())
const [staleStacks, setStaleStacks] = useState(new Set())
const [reprocessSources, setReprocessSources] = useState(new Set())
const [generating, setGenerating] = useState({}) // { 'source:name': true }
async function handleLogin(user, pass) {
setCredentials(user, pass)
await api.getSources().then(s => {
sessionStorage.setItem('df_user', user)
sessionStorage.setItem('df_pass', pass)
setSources(s)
if (!source && s.length > 0) setSource(s[0].name)
setAuthed(true)
setLoginUser(user)
})
}
function handleLogout() {
clearCredentials()
sessionStorage.removeItem('df_user')
sessionStorage.removeItem('df_pass')
setAuthed(false)
setLoginUser('')
setSources([])
setStaleSources(new Set())
setStaleStacks(new Set())
setReprocessSources(new Set())
}
// Load initial stale state from DB once on login
useEffect(() => {
if (!authed) return
api.getStatus().then(s => {
setStaleSources(new Set((s.stale_sources || []).map(x => x.name)))
setStaleStacks(new Set((s.stale_stacks || []).map(x => x.name)))
}).catch(() => {})
}, [authed])
function markSourceStale(name) {
setStaleSources(prev => new Set([...prev, name]))
}
function markNeedsReprocess(name) {
setReprocessSources(prev => new Set([...prev, name]))
}
async function handleReprocessSource(name) {
setGenerating(g => ({ ...g, [`rp:${name}`]: true }))
try {
await api.reprocess(name)
setReprocessSources(prev => { const n = new Set(prev); n.delete(name); return n })
} catch (e) { alert(e.message) }
finally { setGenerating(g => { const n = { ...g }; delete n[`rp:${name}`]; return n }) }
}
function markStackStale(name) {
setStaleStacks(prev => new Set([...prev, name]))
}
function clearStackStale(name) {
setStaleStacks(prev => { const n = new Set(prev); n.delete(name); return n })
}
async function handleGenerateSource(name) {
setGenerating(g => ({ ...g, [`src:${name}`]: true }))
try {
await api.generateView(name)
setStaleSources(prev => { const n = new Set(prev); n.delete(name); return n })
} catch (e) { alert(e.message) }
finally { setGenerating(g => { const n = { ...g }; delete n[`src:${name}`]; return n }) }
}
async function handleGenerateStack(name) {
setGenerating(g => ({ ...g, [`stk:${name}`]: true }))
try {
await api.generateStackView(name)
setStaleStacks(prev => { const n = new Set(prev); n.delete(name); return n })
} catch (e) { alert(e.message) }
finally { setGenerating(g => { const n = { ...g }; delete n[`stk:${name}`]; return n }) }
}
// On mount, restore session if credentials are saved
useEffect(() => {
const user = sessionStorage.getItem('df_user')
const pass = sessionStorage.getItem('df_pass')
if (user && pass) handleLogin(user, pass).catch(() => handleLogout())
}, [])
useEffect(() => {
if (source) localStorage.setItem('selectedSource', source)
}, [source])
if (!authed) return