feat(CC-071): add Integrations Overview page
This commit is contained in:
parent
b2587ef629
commit
fb1bedf6aa
1 changed files with 150 additions and 0 deletions
150
src/app/dashboard/integrations/page.tsx
Normal file
150
src/app/dashboard/integrations/page.tsx
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
'use client';
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Link from 'next/link';
|
||||
import { apiFetch } from '../../../lib/api';
|
||||
import { useSession } from '../../../components/session-provider';
|
||||
|
||||
interface DataSource {
|
||||
id: string; name: string; sourceType: string; description: string | null;
|
||||
status: 'active' | 'paused' | 'error' | 'archived'; lastSyncAt: string | null;
|
||||
recordCount: number | null; createdAt: string;
|
||||
}
|
||||
|
||||
const NATIVE_INTEGRATIONS = [
|
||||
{ key: 'clickhouse', name: 'ClickHouse (supersrv)', icon: '🗄️', category: 'Database', status: 'connected', description: 'Business intelligence — 147M+ rânduri, date companiei, ESCO.' },
|
||||
{ key: 'anythingllm', name: 'AnythingLLM', icon: '🤖', category: 'AI / RAG', status: 'connected', description: 'Agenți AI și workspace-uri peste ClickHouse via MCP.' },
|
||||
{ key: 'hermes', name: 'Hermes (CEO-OS AI)', icon: '⚡', category: 'AI', status: 'connected', description: 'Router inteligent Gemini/OpenRouter pentru toate request-urile AI.' },
|
||||
{ key: 'forgejo', name: 'Forgejo (Git)', icon: '🔀', category: 'DevOps', status: 'connected', description: 'Repo de cod — ceo-api + ceo-web, CI/CD via Coolify.' },
|
||||
{ key: 'coolify', name: 'Coolify', icon: '🚀', category: 'DevOps', status: 'connected', description: 'Deploy automat la push — staging + production.' },
|
||||
{ key: 'openrouter', name: 'OpenRouter', icon: '🌐', category: 'AI', status: 'connected', description: 'Fallback LLM routing pentru modele multiple.' },
|
||||
{ key: 'n8n', name: 'n8n Workflows', icon: '⚙️', category: 'Automation', status: 'connected', description: '8179 template-uri de automatizare disponibile.' },
|
||||
{ key: 'metabase', name: 'Metabase', icon: '📊', category: 'Analytics', status: 'connected', description: 'Dashboard-uri BI pe date ClickHouse (OSS, fără AI).' },
|
||||
];
|
||||
|
||||
const PLANNED_INTEGRATIONS = [
|
||||
{ key: 'gmail', name: 'Gmail', icon: '📧', category: 'Email', description: 'Sincronizare email și automatizare răspunsuri.' },
|
||||
{ key: 'gcal', name: 'Google Calendar', icon: '📅', category: 'Productivity', description: 'Sincronizare evenimente și meetings.' },
|
||||
{ key: 'notion', name: 'Notion', icon: '📝', category: 'Productivity', description: 'Export și sincronizare documente.' },
|
||||
{ key: 'slack', name: 'Slack', icon: '💬', category: 'Communication', description: 'Notificări și comenzi din Slack.' },
|
||||
{ key: 'stripe', name: 'Stripe', icon: '💳', category: 'Payments', description: 'Sincronizare plăți și facturare.' },
|
||||
{ key: 'hubspot', name: 'HubSpot', icon: '🎯', category: 'CRM', description: 'Sincronizare contacte și pipeline CRM.' },
|
||||
];
|
||||
|
||||
const STATUS_CLS: Record<string, string> = {
|
||||
active: 'bg-signal-ok/10 text-signal-ok', connected: 'bg-signal-ok/10 text-signal-ok',
|
||||
paused: 'bg-warn/10 text-warn', error: 'bg-signal-danger/10 text-signal-danger',
|
||||
archived: 'bg-muted text-ink-faint',
|
||||
};
|
||||
|
||||
export default function IntegrationsPage() {
|
||||
const { activeTenant } = useSession();
|
||||
const tenantId = activeTenant?.tenantId ?? '';
|
||||
|
||||
const { data: sources = [] } = useQuery({
|
||||
queryKey: ['data-sources', tenantId],
|
||||
queryFn: () => apiFetch<DataSource[]>('/v1/data-sources', { tenantId }),
|
||||
enabled: Boolean(tenantId), staleTime: 60_000,
|
||||
});
|
||||
|
||||
const activeSources = sources.filter((s) => s.status === 'active');
|
||||
const errorSources = sources.filter((s) => s.status === 'error');
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl space-y-8 p-6">
|
||||
<div>
|
||||
<h1 className="font-display text-2xl font-semibold text-ink">Integrări</h1>
|
||||
<p className="text-sm text-ink-faint mt-1">
|
||||
{NATIVE_INTEGRATIONS.length} sisteme native conectate · {activeSources.length} surse de date active
|
||||
{errorSources.length > 0 && <span className="ml-1 text-signal-danger font-semibold">{errorSources.length} erori</span>}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Native/infrastructure integrations */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-wider text-ink-faint">Infrastructură & AI</h2>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
{NATIVE_INTEGRATIONS.map((integration) => (
|
||||
<div key={integration.key} className="card p-4 flex items-start gap-3">
|
||||
<span className="text-2xl">{integration.icon}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 justify-between">
|
||||
<p className="text-sm font-semibold text-ink">{integration.name}</p>
|
||||
<span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${STATUS_CLS[integration.status]}`}>
|
||||
{integration.status}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-[10px] text-ink-faint/70 mt-0.5">{integration.category}</p>
|
||||
<p className="text-xs text-ink-faint mt-1">{integration.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* User-added data sources */}
|
||||
{sources.length > 0 && (
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-wider text-ink-faint">Surse de date înregistrate</h2>
|
||||
<Link href="/dashboard/data/sources" className="text-xs text-primary hover:underline">Gestionează →</Link>
|
||||
</div>
|
||||
<div className="card divide-y divide-border/50">
|
||||
{sources.map((s) => (
|
||||
<div key={s.id} className="p-3 flex items-center justify-between gap-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-ink">{s.name}</p>
|
||||
<p className="text-[10px] text-ink-faint">{s.sourceType} {s.lastSyncAt ? `· sincronizat ${new Date(s.lastSyncAt).toLocaleDateString('ro-RO')}` : ''}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{s.recordCount != null && (
|
||||
<span className="text-xs font-mono text-ink-faint">{s.recordCount.toLocaleString('ro-RO')}</span>
|
||||
)}
|
||||
<span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${STATUS_CLS[s.status] ?? 'bg-muted text-ink-faint'}`}>
|
||||
{s.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Planned integrations */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-wider text-ink-faint">Viitoare integrări</h2>
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
{PLANNED_INTEGRATIONS.map((integration) => (
|
||||
<div key={integration.key} className="card p-4 opacity-60 flex items-start gap-3">
|
||||
<span className="text-2xl grayscale">{integration.icon}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 justify-between">
|
||||
<p className="text-sm font-semibold text-ink">{integration.name}</p>
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-ink-faint">planificat</span>
|
||||
</div>
|
||||
<p className="text-[10px] text-ink-faint/70 mt-0.5">{integration.category}</p>
|
||||
<p className="text-xs text-ink-faint mt-1">{integration.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Quick links */}
|
||||
<section className="card p-5 space-y-3">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-ink-faint">Acțiuni rapide</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Link href="/dashboard/data/sources" className="rounded-lg border px-4 py-2 text-sm text-ink hover:bg-muted/50 transition-colors">
|
||||
+ Adaugă sursă de date
|
||||
</Link>
|
||||
<Link href="/dashboard/data/registry" className="rounded-lg border px-4 py-2 text-sm text-ink hover:bg-muted/50 transition-colors">
|
||||
Registry date
|
||||
</Link>
|
||||
<Link href="/dashboard/reports/exports" className="rounded-lg border px-4 py-2 text-sm text-ink hover:bg-muted/50 transition-colors">
|
||||
Export date
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue