feat(CC-083): add Memory & Knowledge page (AGE placeholder + current knowledge modules)

This commit is contained in:
admin-valentin 2026-08-02 17:22:14 +00:00
parent 5a124fd015
commit de0a635069

View file

@ -0,0 +1,100 @@
'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>
);
}