import { useState, useEffect } from 'react' import { Link } from 'react-router-dom' import { api } from '../api' // Importing is the frequent job; configuring a source is the rare one. This is // the top-level entry point for the frequent one — every source in one place, // with a sync button for anything on a bank feed. export default function ImportHub({ sources }) { const [stats, setStats] = useState({}) // name -> stats const [lastImport, setLastImport] = useState({}) // name -> ISO timestamp const [busy, setBusy] = useState('') const [results, setResults] = useState({}) // name -> message const [errors, setErrors] = useState({}) // name -> message useEffect(() => { let cancelled = false Promise.all(sources.map(s => api.getStats(s.name).then(st => [s.name, st]).catch(() => [s.name, null]) )).then(pairs => { if (!cancelled) setStats(Object.fromEntries(pairs)) }) api.getAllImportLog().then(log => { if (cancelled) return const latest = {} for (const entry of log) { if (!latest[entry.source_name] || entry.imported_at > latest[entry.source_name]) { latest[entry.source_name] = entry.imported_at } } setLastImport(latest) }).catch(() => {}) return () => { cancelled = true } }, [sources]) async function sync(name) { setBusy(name) setErrors(e => ({ ...e, [name]: '' })) setResults(r => ({ ...r, [name]: '' })) try { const res = await api.syncSimpleFin(name, { days: 10 }) setResults(r => ({ ...r, [name]: `${res.imported} imported, ${res.duplicates} already had` + (res.errors?.length ? ` — ${res.errors.join('; ')}` : ''), })) api.getStats(name).then(st => setStats(s => ({ ...s, [name]: st }))).catch(() => {}) api.getAllImportLog().then(log => { const entry = log.find(l => l.source_name === name) if (entry) setLastImport(l => ({ ...l, [name]: entry.imported_at })) }).catch(() => {}) } catch (err) { setErrors(e => ({ ...e, [name]: err.message })) } finally { setBusy('') } } const feeds = sources.filter(s => s.config?.simplefin?.account_id) const manual = sources.filter(s => !s.config?.simplefin?.account_id) function Row({ s, isFeed }) { const st = stats[s.name] const when = lastImport[s.name] return (
{s.name}
{st ? `${st.total_records} records` : '—'} {st && Number(st.pending_records) > 0 && ` · ${st.pending_records} untransformed`} {when && ` · last import ${new Date(when).toLocaleDateString()}`}
{results[s.name] && {results[s.name]}} {errors[s.name] && {errors[s.name]}} {isFeed ? ( ) : ( Upload CSV )}
) } return (

Import

{feeds.length > 0 && (

Bank feeds

{feeds.map(s => )}

Syncs pull the last 10 days; use a source’s Import tab to backfill further.

)} {manual.length > 0 && (

CSV sources

{manual.map(s => )}
)} {sources.length === 0 &&

No sources yet.

} Full import history →
) }