100 lines
5.7 KiB
TypeScript
100 lines
5.7 KiB
TypeScript
'use client';
|
|
|
|
import { useMemo } from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import Link from 'next/link';
|
|
import { apiFetch } from '../../../lib/api';
|
|
import { useSession } from '../../../components/session-provider';
|
|
|
|
interface ResearchBrief { id: string; title: string; subjectType: string | null; subjectId: string | null; tags: string[]; createdAt: string; }
|
|
interface Decision { id: string; title: string; rationale: string | null; tags: string[]; createdAt: string; }
|
|
interface Observation { id: string; metric: string; value: string; subjectType: string; source: string | null; createdAt: string; }
|
|
|
|
export default function MemoryKnowledgePage() {
|
|
const { activeTenant } = useSession();
|
|
const tenantId = activeTenant?.tenantId ?? '';
|
|
|
|
const { data: briefs = [] } = useQuery({ queryKey: ['mem-briefs', tenantId], queryFn: () => apiFetch<ResearchBrief[]>('/v1/research-briefs', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 });
|
|
const { data: decisions = [] } = useQuery({ queryKey: ['mem-decisions', tenantId], queryFn: () => apiFetch<Decision[]>('/v1/decisions?limit=200', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 });
|
|
const { data: observations = [] } = useQuery({ queryKey: ['mem-obs', tenantId], queryFn: () => apiFetch<Observation[]>('/v1/observations', { tenantId }), enabled: Boolean(tenantId), staleTime: 120_000 });
|
|
|
|
const stats = useMemo(() => {
|
|
const facts = observations.filter((o) => o.source && o.source.trim().length > 0);
|
|
const uniqueTypes = new Set(observations.map((o) => o.subjectType)).size;
|
|
const uniqueMetrics = new Set(observations.map((o) => o.metric)).size;
|
|
const withRationale = decisions.filter((d) => d.rationale && d.rationale.length > 20);
|
|
return { facts, uniqueTypes, uniqueMetrics, withRationale };
|
|
}, [observations, decisions]);
|
|
|
|
return (
|
|
<div className="max-w-4xl space-y-6 p-6">
|
|
<div>
|
|
<h1 className="font-display text-2xl font-semibold text-ink">Memory & Knowledge</h1>
|
|
<p className="text-sm text-ink-faint mt-1">Graf de cunoaștere CEO OS — fapte, relații și context cu provenance.</p>
|
|
</div>
|
|
|
|
<div className="card p-4 bg-warn/5 border-warn/30 space-y-1">
|
|
<p className="text-xs font-semibold text-warn">Apache AGE — neconectat la ceo-api</p>
|
|
<p className="text-xs text-ink-faint">
|
|
Apache AGE (PostgreSQL extension for graph queries) rulează pe server.
|
|
Conexiunea la ceo-api și UI-ul graf complet (entități/relații/provenance) sunt în dezvoltare.
|
|
Datele curente sunt stocate în PostgreSQL standard și accesibile prin modulele de mai jos.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Current knowledge stats */}
|
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
|
<div className="card p-4 text-center">
|
|
<p className="text-2xl font-bold text-ink">{observations.length}</p>
|
|
<p className="text-[10px] text-ink-faint">fapte logate</p>
|
|
</div>
|
|
<div className="card p-4 text-center">
|
|
<p className="text-2xl font-bold text-ink">{briefs.length}</p>
|
|
<p className="text-[10px] text-ink-faint">research briefs</p>
|
|
</div>
|
|
<div className="card p-4 text-center">
|
|
<p className="text-2xl font-bold text-ink">{stats.withRationale.length}</p>
|
|
<p className="text-[10px] text-ink-faint">decizii documentate</p>
|
|
</div>
|
|
<div className="card p-4 text-center">
|
|
<p className="text-2xl font-bold text-ink">{stats.uniqueMetrics}</p>
|
|
<p className="text-[10px] text-ink-faint">tipuri metrici</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Modules currently available */}
|
|
<div className="space-y-2">
|
|
<p className="text-xs font-semibold uppercase tracking-wider text-ink-faint">Module de cunoaștere disponibile acum</p>
|
|
<div className="grid gap-3 sm:grid-cols-2">
|
|
{[
|
|
{ label: 'Research Briefs', href: '/dashboard/intelligence/insights', desc: `${briefs.length} briefs`, icon: '📚' },
|
|
{ label: 'Decizii documentate', href: '/dashboard/decisions', desc: `${stats.withRationale.length} cu rațional`, icon: '🧠' },
|
|
{ label: 'Observații personale', href: '/dashboard/personal-kpi', desc: `${observations.length} fapte`, icon: '📊' },
|
|
{ label: 'Intelligence Persoane', href: '/dashboard/intelligence/people', desc: 'Contacte + observații', icon: '👤' },
|
|
{ label: 'Intelligence Companii', href: '/dashboard/intelligence/companies', desc: 'ClickHouse 147M+', icon: '🏢' },
|
|
{ label: 'AnythingLLM RAG', href: '/dashboard/integrations', desc: 'Q&A pe documente', icon: '🔍' },
|
|
].map((m) => (
|
|
<Link key={m.href} href={m.href} className="card p-4 flex items-center gap-3 hover:border-primary/40 transition-colors">
|
|
<span className="text-2xl">{m.icon}</span>
|
|
<div>
|
|
<p className="text-sm font-medium text-ink">{m.label}</p>
|
|
<p className="text-[10px] text-ink-faint">{m.desc}</p>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Architecture note */}
|
|
<div className="card p-4 bg-primary/5 border-primary/20 space-y-2">
|
|
<p className="text-xs font-semibold text-ink">Arhitectură Memory Graph (planificată)</p>
|
|
<div className="text-[10px] text-ink-faint space-y-1 font-mono">
|
|
<p>PostgreSQL + Apache AGE → Cypher queries</p>
|
|
<p>Entități: Person, Company, Document, Decision, Observation</p>
|
|
<p>Relații: WORKS_AT, MENTIONED_IN, SUPPORTS, CONFLICTS_WITH, CAUSED_BY</p>
|
|
<p>Provenance: source + timestamp + confidence pe fiecare fap</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|