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}
}No data
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 (No data
return ({activeTab.hint}