'use client'; import { useEffect, useState } from 'react'; import { AdminShell } from '@/components/AdminShell'; import { getDataQuality } from '@/lib/api'; function Pct({ value }: { value: number }) { const pct = (value * 100).toFixed(1); const color = value < 0.05 ? 'text-green-400' : value < 0.2 ? 'text-yellow-400' : 'text-red-400'; return {pct}%; } function PctGood({ value }: { value: number }) { const pct = (value * 100).toFixed(1); const color = value > 0.95 ? 'text-green-400' : value > 0.8 ? 'text-yellow-400' : 'text-red-400'; return {pct}%; } function formatTtl(sec: number): string { if (sec < 60) return `${sec}s`; if (sec < 3600) return `${Math.round(sec / 60)}m`; if (sec < 86400) return `${Math.round(sec / 3600)}h`; return `${Math.round(sec / 86400)}d`; } export default function DataQualityPage() { const [data, setData] = useState> | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { getDataQuality() .then(setData) .catch((e) => setError(e.message)) .finally(() => setLoading(false)); }, []); return (

Data quality

{error &&

{error}

} {loading &&

Loading…

} {data && ( <>
Scoring calls (30d)
{data.scoringCallsLast30d}
Missing feature rate
Integration tokens
{data.totalTokens}
Stale token rate (>7d)
{/* Profile freshness — #81 phase B.4 */}

Profile feature freshness

Eligible = users with any tip activity in the last 30 days. Stale = stored row past its TTL. Missing = no row computed yet.

{data.profileFreshness.map((r) => { const fresh = r.totalEligible - r.missing - r.stale; const coverage = r.totalEligible > 0 ? fresh / r.totalEligible : 0; return ( ); })} {data.profileFreshness.length === 0 && ( )}
Feature TTL Eligible Missing Stale Coverage
{r.feature} {formatTtl(r.ttlSec)} {r.totalEligible} 0 ? 'text-orange-400' : 'text-gray-500'}`}>{r.missing} 0 ? 'text-yellow-400' : 'text-gray-500'}`}>{r.stale}
No features registered

Daily feature completeness (14d)

{data.dailyQuality.map((row) => { const coverage = row.total > 0 ? row.withFeatures / row.total : 0; return ( ); })} {data.dailyQuality.length === 0 && ( )}
Date Scoring calls With features Coverage Avg candidates
{row.date} {row.total} {row.withFeatures} {row.avgCandidates?.toFixed(1) ?? '—'}
No data yet
)}
); }