import { useState, useRef, useCallback, useEffect } from 'react'; import { useAppStore, type AdbConfig, type AdbTable } from '@/stores/app'; import { embeddingsApi, type EmbeddingDoc } from '@/api/endpoints/embeddings'; import { useI18n } from '@/i18n'; import { Dna, Shield, ScrollText, Upload, Loader2, Check, X, Trash2, RefreshCw, Link, FileUp, Database, Search, AlertTriangle, } from 'lucide-react'; /* ── Helpers ── */ function Msg({ type, text }: { type: 's' | 'e' | 'i'; text: string }) { const bg = type === 's' ? 'var(--gnl)' : type === 'e' ? 'var(--rdl)' : 'var(--bg2)'; const fg = type === 's' ? 'var(--gn)' : type === 'e' ? 'var(--rd)' : 'var(--t2)'; return (
{type === 's' && } {type === 'e' && } {type === 'i' && } {text}
); } type MsgState = { type: 's' | 'e' | 'i'; text: string } | null; function getActiveTables(cfg: AdbConfig): AdbTable[] { return (cfg.tables || []).filter((t) => t.is_active); } function getAllTables(cfg: AdbConfig): AdbTable[] { return cfg.tables || []; } /* ── Main ── */ export default function EmbeddingsPage() { const { t } = useI18n(); const { adbCfg, ociCfg, genaiCfg } = useAppStore(); // ── Existing Embeddings section ── const [selVid, setSelVid] = useState(() => adbCfg.find((c) => c.is_active)?.id || adbCfg[0]?.id || ''); const [selTable, setSelTable] = useState(''); const [docs, setDocs] = useState([]); const [total, setTotal] = useState(0); const [listLoading, setListLoading] = useState(false); const [listMsg, setListMsg] = useState(null); // ── CIS Recommendations section ── const [cisVid, setCisVid] = useState(() => adbCfg.find((c) => c.is_active)?.id || adbCfg[0]?.id || ''); const cisPdfRef = useRef(null); const [cisUploading, setCisUploading] = useState(false); const [cisMsg, setCisMsg] = useState(null); // ── Knowledge Base section ── const [kbVid, setKbVid] = useState(() => adbCfg.find((c) => c.is_active)?.id || adbCfg[0]?.id || ''); const kbFileRef = useRef(null); const [kbUrl, setKbUrl] = useState(''); const [kbFileUploading, setKbFileUploading] = useState(false); const [kbUrlUploading, setKbUrlUploading] = useState(false); const [kbMsg, setKbMsg] = useState(null); // ── Purge section ── const [purgeVid, setPurgeVid] = useState(() => adbCfg.find((c) => c.is_active)?.id || adbCfg[0]?.id || ''); const [purgeTable, setPurgeTable] = useState(''); const [purgeTenancy, setPurgeTenancy] = useState(''); const [purging, setPurging] = useState(false); const [purgeMsg, setPurgeMsg] = useState(null); const [purgeConfirm, setPurgeConfirm] = useState(false); // Update selTable when selVid changes useEffect(() => { const cfg = adbCfg.find((c) => c.id === selVid); if (cfg) { const tables = getActiveTables(cfg); setSelTable(tables[0]?.table_name || ''); } }, [selVid, adbCfg]); // Preconditions if (!adbCfg.length) { return (

{t('emb.noAdb')}

{t('emb.noAdbHint')}

); } if (!genaiCfg.length && !ociCfg.length) { return (

{t('emb.noOci')}

{t('emb.noOciHint')}

); } /* ── Handlers ── */ const loadEmbs = async () => { if (!selVid) return; setListLoading(true); setListMsg(null); setDocs([]); try { const d = await embeddingsApi.list(selVid, selTable || undefined); setDocs(d.documents); setTotal(d.total); if (!d.documents.length) setListMsg({ type: 'i', text: t('emb.noEmbeddings') }); } catch (err) { setListMsg({ type: 'e', text: err instanceof Error ? err.message : t('emb.errorLoadEmb') }); } finally { setListLoading(false); } }; const deleteEmb = async (docId: string) => { if (!confirm(t('emb.confirmDeleteEmb'))) return; try { await embeddingsApi.remove(selVid, docId, selTable || undefined); loadEmbs(); } catch (err) { alert(err instanceof Error ? err.message : t('common.errorDelete')); } }; const uploadCis = async () => { const f = cisPdfRef.current?.files?.[0]; if (!cisVid || !f) { setCisMsg({ type: 'e', text: t('emb.selectAdbAndFile') }); return; } setCisUploading(true); setCisMsg({ type: 'i', text: t('emb.processing') }); try { const d = await embeddingsApi.uploadFile(cisVid, 'cisrecom', f); setCisMsg({ type: 's', text: `${d.message} -> tabela CISRECOM` }); if (cisPdfRef.current) cisPdfRef.current.value = ''; } catch (err) { setCisMsg({ type: 'e', text: err instanceof Error ? err.message : 'Erro' }); } finally { setCisUploading(false); } }; const uploadKbFile = async () => { const f = kbFileRef.current?.files?.[0]; if (!kbVid || !f) { setKbMsg({ type: 'e', text: t('emb.selectAdbAndFile') }); return; } setKbFileUploading(true); setKbMsg({ type: 'i', text: t('emb.uploading') }); try { const d = await embeddingsApi.uploadFile(kbVid, 'engineerknowledgebase', f); setKbMsg({ type: 's', text: d.message }); if (kbFileRef.current) kbFileRef.current.value = ''; } catch (err) { setKbMsg({ type: 'e', text: err instanceof Error ? err.message : 'Erro' }); } finally { setKbFileUploading(false); } }; const uploadKbUrl = async () => { if (!kbVid || !kbUrl.trim()) { setKbMsg({ type: 'e', text: t('emb.selectAdbAndUrl') }); return; } setKbUrlUploading(true); setKbMsg({ type: 'i', text: t('emb.fetchingUrl') }); try { const d = await embeddingsApi.uploadUrl(kbVid, 'engineerknowledgebase', kbUrl.trim()); setKbMsg({ type: 's', text: d.message }); setKbUrl(''); } catch (err) { setKbMsg({ type: 'e', text: err instanceof Error ? err.message : 'Erro' }); } finally { setKbUrlUploading(false); } }; const executePurge = async () => { if (!purgeVid || !purgeTable) { setPurgeMsg({ type: 'e', text: t('emb.selectTable') }); return; } setPurging(true); setPurgeMsg({ type: 'i', text: t('emb.purging') }); try { const d = await embeddingsApi.purge(purgeVid, purgeTable, purgeTenancy || undefined); setPurgeMsg({ type: 's', text: `${d.deleted} embeddings removidos da tabela ${d.table} (tenancy: ${d.tenancy})` }); setPurgeConfirm(false); } catch (err) { setPurgeMsg({ type: 'e', text: err instanceof Error ? err.message : 'Erro' }); } finally { setPurging(false); } }; /* ── Shared styles ── */ const inputCls = 'w-full px-3 py-2 rounded-lg text-[.78rem] outline-none transition-colors focus:ring-1'; const inputStyle = { background: 'var(--bg2)', border: '1.5px solid var(--bd)', color: 'var(--t1)' }; const labelCls = 'block text-[.72rem] font-semibold mb-1'; const labelStyle = { color: 'var(--t3)' }; const cardCls = 'card'; const cardStyle = { padding: 0 }; const cardHeaderCls = 'card-header'; const cardHeaderStyle = { margin: 0, padding: '14px 20px' }; const adbOptions = adbCfg.map((c) => ( )); const tableOptions = (vid: string) => { const cfg = adbCfg.find((c) => c.id === vid); if (!cfg) return null; return getActiveTables(cfg).map((t) => ( )); }; const allTableOptions = (vid: string) => { const cfg = adbCfg.find((c) => c.id === vid); if (!cfg) return null; return getAllTables(cfg).map((t) => ( )); }; return (
{/* Header */}

{t('emb.title')}

{t('emb.subtitle')}
{/* ────── Existing Embeddings ────── */}
{t('emb.existing')}

{t('emb.existingDesc')}

{listMsg && } {docs.length > 0 && ( <>
{docs.map((doc) => { let meta = doc.metadata || '\u2014'; if (typeof meta === 'object') meta = JSON.stringify(meta); if (meta.length > 200) meta = meta.substring(0, 200) + '\u2026'; return ( ); })}
ID Metadata
{doc.id.substring(0, 12)} {meta}

{t('emb.totalDocs').replace('{0}', String(total))}

)}
{/* ────── CIS Recommendations ────── */}
{t('emb.cisTitle')}

{t('emb.cisDesc')}

{cisMsg && }
{/* ────── Knowledge Base ────── */}
{t('emb.kbTitle')}

{t('emb.kbDesc')}

{kbMsg && }
{/* File upload */}
{/* Divider */}
{/* URL import */}
setKbUrl(e.target.value)} placeholder="https://docs.oracle.com/..." className={inputCls} style={inputStyle} />
{/* ────── Purge ────── */}
{t('emb.purgeTitle')}

{t('emb.purgeDesc')}

{purgeMsg && }
setPurgeTenancy(e.target.value)} placeholder="Filtrar por tenancy" className={inputCls} style={inputStyle} />
{!purgeConfirm ? ( ) : (
)}
); }