import { useCallback, useEffect, useMemo, useState } from 'react' import { Users, UserCog, Package, ShoppingCart, DollarSign, Activity, Boxes, RefreshCw, Search, Loader2, TrendingUp, BarChart3, Network, HardDrive, ShieldCheck, Radio, GitBranch, Lock, Layers, } from 'lucide-react' import { cn } from '../../lib/utils' import { TrinoFederationView, type SubTab } from './TrinoFederationView' import { LiveDashboard } from './LiveDashboard' import { LineageView } from './LineageView' import { GovernanceOwnershipView } from './GovernanceOwnershipView' import { GovernanceAccessView } from './GovernanceAccessView' import { ObservabilityView } from './ObservabilityView' import { IcebergNessieView } from './IcebergNessieView' type ExplorerTab = 'business' | 'live' | SubTab | 'lineage' | 'ownership' | 'access' | 'observability' | 'iceberg' const TABS: { id: ExplorerTab; label: string; icon: typeof Users; hint: string; live?: boolean }[] = [ { id: 'business', label: 'Business Overview', icon: BarChart3, hint: 'Customers, orders, workforce, supply chain & telemetry across every source' }, { id: 'live', label: 'Live', icon: Radio, hint: 'Realtime business activity — live counters, ingestion throughput & region matrix', live: true }, { id: 'federated', label: 'Federated (Trino)', icon: Network, hint: 'One SQL across all 5 databases + region scorecard joined live' }, { id: 'lake', label: 'Hadoop Lake', icon: HardDrive, hint: 'All business data mirrored as external Iceberg tables on HDFS' }, { id: 'iceberg', label: 'Iceberg', icon: Layers, hint: 'Nessie catalog · snapshots · manifests · data files · time travel' }, { id: 'dictionary', label: 'Data Dictionary', icon: ShieldCheck, hint: 'Every table & column with PII / masking status — exactly what the assistant sees' }, { id: 'lineage', label: 'Lineage', icon: GitBranch, hint: 'End-to-end data lineage with column-level PII tracing from source to curated layer' }, { id: 'ownership', label: 'Ownership', icon: UserCog, hint: 'Data owners, stewards, tiers & business glossary — accountability per dataset' }, { id: 'access', label: 'Access & Policies', icon: Lock, hint: 'Governance posture: PII masking, ownership, live DQ & alerts vs each data contract' }, { id: 'observability', label: 'Observability', icon: Activity, hint: 'Volume, freshness & schema-drift monitoring with live alerts across every table' }, ] type Bucket = { key: string; count: number; value?: number } type Business = { ok: boolean generated_at: string filters: { q: string; region: string } kpis: { customers: number employees: number products: number orders_indexed: number revenue_indexed: number avg_order: number telemetry_indexed: number supply_indexed: number source_totals: { orders?: number; hr_events?: number; supply_events?: number } } orders: { by_region: Bucket[] by_channel: Bucket[] by_status: Bucket[] top_customers: Bucket[] top_products: Bucket[] over_time: Bucket[] } hr: { by_department: Bucket[]; by_role: Bucket[]; by_event: Bucket[] } supply: { by_type: Bucket[]; by_region: Bucket[] } telemetry: { by_metric: Bucket[] } regions: string[] } const REGION_COLORS = ['#34d399', '#60a5fa', '#f472b6', '#fbbf24', '#a78bfa', '#22d3ee', '#fb7185'] function fmtNum(n?: number | null) { if (n == null) return '—' if (Math.abs(n) >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(2)}B` if (Math.abs(n) >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M` if (Math.abs(n) >= 1_000) return `${(n / 1_000).toFixed(1)}K` return String(n) } function fmtMoney(n?: number | null) { if (n == null) return '—' return `€${fmtNum(n)}` } function monthLabel(key: string) { const m = /^(\d{4})-(\d{2})/.exec(key) if (!m) return key const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] return `${months[Number(m[2]) - 1]} '${m[1].slice(2)}` } function Kpi({ icon: Icon, label, value, sub, accent }: { icon: typeof Users; label: string; value: string; sub?: string; accent: string }) { return (

{label}

{value}

{sub &&

{sub}

}
) } function Panel({ title, subtitle, children, icon: Icon }: { title: string; subtitle?: string; children: React.ReactNode; icon?: typeof Users }) { return (
{Icon && }

{title}

{subtitle && {subtitle}}
{children}
) } function BarsH({ data, valueKind, colorByIndex }: { data: Bucket[]; valueKind?: 'money' | 'num'; colorByIndex?: boolean }) { const useVal = valueKind != null const max = Math.max(1, ...data.map((d) => (useVal && d.value != null ? d.value : d.count))) if (!data.length) return

No data

return (
{data.map((d, i) => { const metric = useVal && d.value != null ? d.value : d.count const pct = Math.max(2, (metric / max) * 100) const color = colorByIndex ? REGION_COLORS[i % REGION_COLORS.length] : '#38bdf8' const label = useVal && d.value != null ? (valueKind === 'money' ? fmtMoney(d.value) : fmtNum(d.value)) : fmtNum(d.count) return (
{d.key ?? '—'}
{label}
) })}
) } function AreaTrend({ data }: { data: Bucket[] }) { const pts = data.filter((d) => d.value != null) if (pts.length < 2) return

Not enough data

const w = 560 const h = 120 const max = Math.max(...pts.map((p) => p.value || 0)) const min = Math.min(...pts.map((p) => p.value || 0)) const range = max - min || 1 const step = w / (pts.length - 1) const coords = pts.map((p, i) => [i * step, h - ((((p.value || 0) - min) / range) * (h - 16) + 8)]) const line = coords.map((c, i) => `${i === 0 ? 'M' : 'L'}${c[0].toFixed(1)},${c[1].toFixed(1)}`).join(' ') const area = `${line} L${w},${h} L0,${h} Z` return (
{coords.map((c, i) => )}
{monthLabel(pts[0].key)} {monthLabel(pts[Math.floor(pts.length / 2)].key)} {monthLabel(pts[pts.length - 1].key)}
) } function Donut({ data }: { data: Bucket[] }) { const total = data.reduce((s, d) => s + d.count, 0) || 1 let acc = 0 const r = 42 const c = 2 * Math.PI * r if (!data.length) return

No data

return (
{data.map((d, i) => { const frac = d.count / total const dash = frac * c const seg = ( ) acc += dash return seg })}
{data.slice(0, 7).map((d, i) => (
{d.key ?? '—'} {((d.count / total) * 100).toFixed(0)}%
))}
) } export function DataExplorerView() { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [region, setRegion] = useState('') const [qInput, setQInput] = useState('') const [q, setQ] = useState('') const [view, setView] = useState('business') const load = useCallback(async () => { setLoading(true) try { const params = new URLSearchParams() if (q) params.set('q', q) if (region) params.set('region', region) const r = await fetch(`/api/search/business?${params.toString()}`) if (r.ok) setData(await r.json()) } catch { /* */ } finally { setLoading(false) } }, [q, region]) useEffect(() => { load() }, [load]) const k = data?.kpis const regions = useMemo(() => data?.regions || [], [data]) const activeTab = TABS.find((t) => t.id === view) || TABS[0] return (
{/* header */}

Data Explorer

{activeTab.hint}

{view === 'business' && (
{ e.preventDefault(); setQ(qInput.trim()) }} className="flex items-center gap-1 rounded-md border border-border bg-surface px-2 py-1" > setQInput(e.target.value)} placeholder="Filter by name, id, region…" className="w-48 bg-transparent text-[11px] text-foreground outline-none placeholder:text-foreground-faint" />
)}
{/* tab bar */}
{TABS.map(({ id, label, icon: Icon, live }) => ( ))}
{/* Live realtime dashboard */} {view === 'live' && } {/* Trino federation / lake / dictionary tabs */} {(view === 'federated' || view === 'lake' || view === 'dictionary') && } {/* Governance / lineage / observability sub-tabs */} {view === 'lineage' &&
} {view === 'ownership' &&
} {view === 'access' &&
} {view === 'observability' &&
} {view === 'iceberg' &&
} {/* ───────── BUSINESS OVERVIEW ───────── */} {view === 'business' && ( <> {/* region filters */}
Region {['', ...regions].map((rg) => ( ))} {(q || region) && ( )} {data && updated {new Date(data.generated_at).toLocaleTimeString()}}
{/* KPIs */}
{/* Sales */}

Sales & Customers — PostgreSQL

{/* HR */}

Workforce — MySQL HR

{/* Supply + telemetry */}

Supply Chain (MongoDB) & Telemetry (Cassandra)

)}
) }