import { useState, useEffect, useCallback } from 'react'
import useTheme from '../theme.jsx'
import useAuth from '../auth.jsx'
export default function StatusBar({ view, sources = [], sourceId, setSourceId, versions = [], versionId }) {
const { dark, setDark } = useTheme()
const { user, logout } = useAuth()
const showVersion = view === 'baseline' || view === 'forecast'
const selectedVersion = versions.find(v => String(v.id) === String(versionId))
const [info, setInfo] = useState(null)
const [showInfo, setShow] = useState(false)
const [copied, setCopied] = useState(false)
// Forecast.jsx mirrors the pivot's own updating state onto this event. The
// viewer shows the same thing as a beaker in its settings panel, which is
// exactly where you cannot see it once the panel is closed.
const [busy, setBusy] = useState(false)
const refreshInfo = useCallback(async () => {
if (!versionId || !showVersion) { setInfo(null); return }
try {
const r = await fetch(`/api/versions/${versionId}/table-info`)
setInfo(r.ok ? await r.json() : null)
} catch { setInfo(null) }
}, [versionId, showVersion])
useEffect(() => { refreshInfo() }, [refreshInfo])
// operations broadcast this after a write so the row count stays honest
useEffect(() => {
const onChange = () => refreshInfo()
window.addEventListener('pf-data-changed', onChange)
return () => window.removeEventListener('pf-data-changed', onChange)
}, [refreshInfo])
useEffect(() => {
const onBusy = (e) => setBusy(!!e.detail?.busy)
window.addEventListener('pf-pivot-busy', onBusy)
return () => window.removeEventListener('pf-pivot-busy', onBusy)
}, [])
async function copyTable() {
if (!info?.fc_table) return
try {
await navigator.clipboard.writeText(info.fc_table)
setCopied(true)
setTimeout(() => setCopied(false), 1200)
} catch {}
}
const fmt = (n) => n == null ? '—' : n.toLocaleString()
return (
{/* Source is a Setup concern: the raw table name means nothing to someone
editing a forecast, and a version carries its own source, so picking a
forecast in the sidebar settles it everywhere else. */}
{view === 'setup' && (
<>
Source
>
)}
{showVersion && (
<>
{/* What is open, and how big it is -- the two things a forecaster
wants from a status bar. The physical table it writes to is an
implementation detail and means nothing to a sales account, so it
has moved into the hover panel and onto the Baseline page, which
is where a version's plumbing is managed. */}
{info && (
<>
| setShow(true)}
onMouseLeave={() => setShow(false)}
className="font-medium text-gray-700 cursor-default"
title="Hover for the write target and row breakdown"
>
{selectedVersion?.name || '—'}
{info.exists ? `${fmt(info.rows)} rows` : 'not created'}
{showInfo && (
Write target
table
{/* Still copyable, just no longer the headline: pasting it
into psql is the reason it was ever on screen. */}
reads from{info.source}total rows{fmt(info.rows)}
{info.by_iter?.length > 0 && (
<>
Rows by iter
{info.by_iter.map(r => (
{r.pf_iter}
{fmt(r.n)}
))}
>
)}
)}
>
)}
>
)}
{/* Left of the ml-auto block, so it sits beside the pivot's own figures
rather than out among the account controls. Occupies no space at all
when idle: a permanently reserved slot reads as a thing that is broken
when empty. */}
{busy && view === 'forecast' && (
Calculating…
)}