import { useCallback, useEffect, useState } from '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 path: string type: 'directory' | 'file' size?: number size_human?: string modified?: string owner?: string group?: string perms?: string replication?: number } type Health = { ok: boolean namenode?: string live_datanodes?: number dead_datanodes?: number capacity_total_human?: string capacity_used_human?: string capacity_used_pct?: number error?: string } export function HdfsView() { const [health, setHealth] = useState(null) const [path, setPath] = useState('/') const [folders, setFolders] = useState([]) const [files, setFiles] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(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 { const r = await fetch('/api/storage/hdfs/health') if (r.ok) setHealth(await r.json()) } catch { /* ignore */ } }, []) const loadList = useCallback(async (p: string) => { setLoading(true) setError(null) try { const r = await fetch(`/api/storage/hdfs/list?path=${encodeURIComponent(p)}`) const j = await r.json() if (!j.ok) { setError(j.error || 'List failed') setFolders([]) setFiles([]) return } setFolders(j.folders || []) setFiles(j.files || []) } catch { setError('HDFS API unavailable') } finally { setLoading(false) } }, []) useEffect(() => { loadHealth() }, [loadHealth]) useEffect(() => { loadList(path) }, [path, loadList]) const openPreview = useCallback(async (p: string) => { setPreviewing(true) setPreview({ path: p, text: '', binary: false, truncated: false }) try { const r = await fetch(`/api/storage/hdfs/preview?path=${encodeURIComponent(p)}`) const j = await r.json() if (j.ok) setPreview({ path: p, text: j.text, binary: j.binary, truncated: j.truncated }) else setPreview({ path: p, text: j.error || 'Preview failed', binary: false, truncated: false }) } catch { setPreview({ path: p, text: 'Preview failed', binary: false, truncated: false }) } finally { setPreviewing(false) } }, []) const crumbs = path === '/' ? [] : path.split('/').filter(Boolean) return (

Hadoop HDFS

{health?.namenode || '10.0.21.61:9870'} · {health?.live_datanodes ?? '—'} datanodes ·{' '} {health?.capacity_used_human || '—'} / {health?.capacity_total_human || '—'} {typeof health?.capacity_used_pct === 'number' ? ` (${health.capacity_used_pct}%)` : ''}

NameNode UI
{tab === 'analytics' && } {tab === 'files' && (
{loading && (

Loading…

)} {error &&

{error}

}
{folders.map((f) => ( ))} {files.map((o) => ( ))}
Name Size Owner Perms Modified
{f.owner} {f.perms} {f.modified?.slice(0, 19).replace('T', ' ') || '—'}
{o.name} {o.size_human} {o.owner} {o.perms} {o.modified?.slice(0, 19).replace('T', ' ') || '—'}
{!loading && folders.length === 0 && files.length === 0 && !error && (

This directory is empty.

)}
)} {preview && (
setPreview(null)}>
e.stopPropagation()}>
{preview.path}
{preview.binary &&

Binary file — showing decoded preview.

}
              {previewing ? 'Loading…' : preview.text}
              {preview.truncated && '\n\n… (truncated)'}
            
)}
) }