import { useCallback, useEffect, useState } from 'react' import { UserCircle, RefreshCw, Loader2, AlertTriangle, BookOpen, ShieldCheck, Check, X } from 'lucide-react' import { cn } from '../../lib/utils' type Pii = { pii_count: number; all_masked: boolean; masked: number; unmasked: number } type DsRow = { key: string; label: string; engine: string; color: string; table: string domain?: string; owner?: string | null; steward?: string | null; team?: string | null tier?: string | null; classification?: string | null; updated_at?: string | null orphan: boolean; pii: Pii } type Summary = { total: number; orphans: number; stewarded: number; owned: number } type GUser = { id: string | null; name: string; display: string; type: string } type Term = { name: string; description: string; domain?: string; related?: string[] } const TIERS = ['Tier1', 'Tier2', 'Tier3'] const CLASSES = ['Public', 'Internal', 'Confidential', 'Restricted'] export function GovernanceOwnershipView() { const [rows, setRows] = useState([]) const [summary, setSummary] = useState(null) const [users, setUsers] = useState([]) const [glossary, setGlossary] = useState([]) const [glossarySource, setGlossarySource] = useState('') const [loading, setLoading] = useState(true) const [omConnected, setOmConnected] = useState(false) const [editKey, setEditKey] = useState(null) const [form, setForm] = useState<{ owner: string; steward: string; team: string; tier: string; classification: string }>( { owner: '', steward: '', team: '', tier: '', classification: '' }, ) const [saving, setSaving] = useState(false) const load = useCallback(async () => { setLoading(true) try { const [d, u, g] = await Promise.all([ fetch('/api/governance/datasets').then((r) => r.json()), fetch('/api/governance/users').then((r) => r.json()), fetch('/api/governance/glossary').then((r) => r.json()), ]) setRows(d.datasets || []) setSummary(d.summary || null) setOmConnected(!!d.om_connected) setUsers(u.users || []) setGlossary(g.terms || []) setGlossarySource(g.source || '') } catch { /* */ } finally { setLoading(false) } }, []) useEffect(() => { load() }, [load]) const openEdit = (r: DsRow) => { setEditKey(r.key) setForm({ owner: r.owner || '', steward: r.steward || '', team: r.team || '', tier: r.tier || '', classification: r.classification || '' }) } const save = async () => { if (!editKey) return setSaving(true) try { await fetch('/api/governance/assign', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ key: editKey, ...form }), }) setEditKey(null) await load() } catch { /* */ } finally { setSaving(false) } } const people = users.filter((u) => u.type === 'user') const teams = users.filter((u) => u.type === 'team') return (
{/* KPIs */}
OpenMetadata {omConnected ? 'connected' : 'offline'} · assignments stored locally{omConnected ? ' + synced to OM' : ''}
{/* ownership matrix */}
{rows.map((r) => ( ))}
Dataset Owner Steward Team Tier PII
{r.label}
{r.table}
{r.owner ? {r.owner} : unassigned} {r.steward || '—'} {r.team || '—'} {r.tier ? {r.tier} : '—'} {r.pii.pii_count > 0 ? ( {r.pii.masked}/{r.pii.pii_count} masked ) : none}
{/* glossary */}

Business glossary {glossarySource && · {glossarySource}}

{glossary.map((t) => (
{t.name} {t.domain && {t.domain}}

{t.description}

{t.related && t.related.length > 0 && (
{t.related.map((rl) => {rl})}
)}
))}
{/* assign modal */} {editKey && (
setEditKey(null)}>
e.stopPropagation()}>

Assign ownership — {rows.find((r) => r.key === editKey)?.label}

)}
) } function Kpi({ icon: Icon, label, value, accent }: { icon: typeof UserCircle; label: string; value: string; accent: string }) { return (

{label}

{value}

) } function Field({ label, children }: { label: string; children: React.ReactNode }) { return (
{children}
) }