{delta} vs {lastWeek.toLocaleString()} previous 7 days
{onClick && }
)
}
function formatWhen(value: string | null | undefined) {
if (!value) return '—'
const d = new Date(value)
return Number.isNaN(d.getTime()) ? '—' : d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })
}
export function AdminDashboardExtras({ publishedAt, draftUpdatedAt, isDirty, onNavigate, onRequestPublish }: Props) {
const [data, setData] = useState(null)
const [failed, setFailed] = useState(false)
useEffect(() => {
let cancelled = false
fetch('/api/admin-dashboard')
.then(r => (r.ok ? r.json() : Promise.reject(new Error('Failed to load dashboard data'))))
.then(json => { if (!cancelled) setData(json as DashboardData) })
.catch(() => { if (!cancelled) setFailed(true) })
return () => { cancelled = true }
}, [])
const draftNewer = !!draftUpdatedAt && (!publishedAt || new Date(draftUpdatedAt).getTime() > new Date(publishedAt).getTime())
const unpublished = isDirty || draftNewer
const attention: { key: string; label: string; tone: 'alert' | 'ok'; action?: () => void; actionLabel?: string }[] = []
if (data) {
if (data.seasonCheck && data.seasonCheck.total > 0) attention.push({ key: 'season-check', label: `Season check: ${data.seasonCheck.total} thing${data.seasonCheck.total === 1 ? '' : 's'} to fix`, tone: 'alert', action: () => onNavigate('podcast:season-check'), actionLabel: 'Open Season Check' })
if (data.comments.pending > 0) attention.push({ key: 'comments', label: `${data.comments.pending} study comment${data.comments.pending === 1 ? '' : 's'} awaiting approval`, tone: 'alert', action: () => onNavigate('study-comments'), actionLabel: 'Review' })
if (data.nuggets.pending > 0) attention.push({ key: 'nuggets', label: `${data.nuggets.pending} Nuggets upload${data.nuggets.pending === 1 ? '' : 's'} waiting in the workflow`, tone: 'alert', action: () => onNavigate('nuggets:workflow'), actionLabel: 'Open workflow' })
if (data.latestEpisode.available && data.latestEpisode.stale) attention.push({ key: 'feed', label: `Podcast feed has no new episode in ${data.latestEpisode.daysAgo} days`, tone: 'alert', action: () => onNavigate('podcast:rss'), actionLabel: 'Check feed' })
if (!data.health.lastBackup.ok) attention.push({ key: 'backup', label: 'Last data backup failed', tone: 'alert' })
if (data.health.dataDirWritable === false) attention.push({ key: 'disk', label: 'Data directory is not writable', tone: 'alert' })
}
return (
<>
{unpublished && (
{isDirty ? 'You have unsaved edits.' : 'Draft changes are not live yet.'} Draft updated {formatWhen(draftUpdatedAt)} · Live version published {formatWhen(publishedAt)}.