feat(hadoop): embedded lakehouse analytics + Impala/Hive vs Trino comparison
Adds an Analytics sub-tab to the Hadoop view with live KPIs and revenue breakdowns (by year/region/category/channel) queried from Trino over iceberg.hadoop.historical_sales, plus a query-engine comparison panel. Trino latency is measured live; Impala and Hive are shown as clearly-labelled representative figures (those engines are not deployed). New cached endpoints /api/hadoop/analytics and /api/hadoop/engines.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { ChevronRight, Download, Eye, FileText, Folder, Loader2, RefreshCw, Server, X } from 'lucide-react'
|
||||
import { BarChart3, ChevronRight, Download, Eye, FileText, Folder, HardDrive, Loader2, RefreshCw, Server, X } from 'lucide-react'
|
||||
import { cn } from '../../lib/utils'
|
||||
import { subTabActive, subTabIdle } from '../../lib/tabActive'
|
||||
import { HadoopAnalytics } from './HadoopAnalytics'
|
||||
|
||||
type HdfsEntry = {
|
||||
name: string
|
||||
@@ -36,6 +37,7 @@ export function HdfsView() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [preview, setPreview] = useState<{ path: string; text: string; binary: boolean; truncated: boolean } | null>(null)
|
||||
const [previewing, setPreviewing] = useState(false)
|
||||
const [tab, setTab] = useState<'files' | 'analytics'>('files')
|
||||
|
||||
const loadHealth = useCallback(async () => {
|
||||
try {
|
||||
@@ -106,7 +108,15 @@ export function HdfsView() {
|
||||
{typeof health?.capacity_used_pct === 'number' ? ` (${health.capacity_used_pct}%)` : ''}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="flex gap-0.5 rounded-md border border-border p-0.5">
|
||||
<button type="button" onClick={() => setTab('files')} className={cn('inline-flex items-center gap-1 rounded px-2.5 py-1 text-[11px]', tab === 'files' ? subTabActive : subTabIdle)}>
|
||||
<HardDrive className="h-3 w-3" /> Files
|
||||
</button>
|
||||
<button type="button" onClick={() => setTab('analytics')} className={cn('inline-flex items-center gap-1 rounded px-2.5 py-1 text-[11px]', tab === 'analytics' ? subTabActive : subTabIdle)}>
|
||||
<BarChart3 className="h-3 w-3" /> Analytics
|
||||
</button>
|
||||
</div>
|
||||
<a href="http://10.0.21.61:9870" target="_blank" rel="noreferrer" className={cn('inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-[11px] font-medium', subTabActive)}>
|
||||
NameNode UI
|
||||
</a>
|
||||
@@ -116,6 +126,9 @@ export function HdfsView() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{tab === 'analytics' && <HadoopAnalytics />}
|
||||
|
||||
{tab === 'files' && (
|
||||
<div className="flex min-h-0 flex-1 flex-col p-3">
|
||||
<nav className="mb-2 flex flex-wrap items-center gap-1 text-[10px] text-foreground-muted">
|
||||
<button type="button" className="font-medium hover:text-emerald-400" onClick={() => setPath('/')}>
|
||||
@@ -207,6 +220,7 @@ export function HdfsView() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{preview && (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/60 p-6" onClick={() => setPreview(null)}>
|
||||
|
||||
Reference in New Issue
Block a user