From 5a124fd015adbbbeca81a89696d9ac5f069efb98 Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Sun, 2 Aug 2026 17:22:13 +0000 Subject: [PATCH] feat(CC-083): add API Connections page (external REST/GraphQL connections with auth config) --- src/app/dashboard/integrations/api/page.tsx | 138 ++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/app/dashboard/integrations/api/page.tsx diff --git a/src/app/dashboard/integrations/api/page.tsx b/src/app/dashboard/integrations/api/page.tsx new file mode 100644 index 0000000..a8d8b6e --- /dev/null +++ b/src/app/dashboard/integrations/api/page.tsx @@ -0,0 +1,138 @@ +'use client'; + +import { useState } from 'react'; +import { useQuery, useMutation, useQueryClient } 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; type: string; status: string; + config: Record | null; lastSyncAt: string | null; createdAt: string; +} + +const API_TYPES = ['rest', 'graphql', 'grpc', 'soap', 'openapi']; + +export default function APIConnectionsPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const qc = useQueryClient(); + const [showCreate, setShowCreate] = useState(false); + const [form, setForm] = useState({ name: '', type: 'rest', baseUrl: '', authType: 'bearer', authValue: '', scope: '' }); + + const { data: sources = [], isLoading } = useQuery({ + queryKey: ['api-conn', tenantId], + queryFn: () => apiFetch('/v1/data-sources', { tenantId }), + enabled: Boolean(tenantId), staleTime: 60_000, + }); + + const apiConns = sources.filter((s) => API_TYPES.includes(s.type) || (s.config as Record)?.authType); + + const createMut = useMutation({ + mutationFn: () => apiFetch('/v1/data-sources', { tenantId, method: 'POST', body: { + name: form.name, type: form.type, + config: { baseUrl: form.baseUrl, authType: form.authType, scope: form.scope || undefined }, + }}), + onSuccess: () => { qc.invalidateQueries({ queryKey: ['api-conn', tenantId] }); setShowCreate(false); setForm({ name: '', type: 'rest', baseUrl: '', authType: 'bearer', authValue: '', scope: '' }); }, + }); + + const testMut = useMutation({ + mutationFn: (id: string) => apiFetch(`/v1/data-sources/${id}/test`, { tenantId, method: 'POST', body: {} }), + }); + + return ( +
+
+
+

Conexiuni API

+

+ {isLoading ? 'Se încarcă…' : `${apiConns.length} API-uri externe conectate`} +

+
+ +
+ + {showCreate && ( +
+

Conexiune API nouă

+
+ setForm((p) => ({ ...p, name: e.target.value }))} + className="rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring" /> + + setForm((p) => ({ ...p, baseUrl: e.target.value }))} + className="rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring sm:col-span-2 font-mono text-xs" /> + + setForm((p) => ({ ...p, scope: e.target.value }))} + className="rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring" /> +
+
+

Cheile și token-urile se stochează criptat. Nu le introduce în câmpul URL.

+
+
+ + +
+
+ )} + + {isLoading ? ( +
Se încarcă…
+ ) : apiConns.length === 0 ? ( +
+

🔌

+

Nicio conexiune API externă configurată.

+

Adaugă API-uri externe (Stripe, HubSpot, etc.) pentru a le folosi în automatizări.

+
+ ) : ( +
+ {apiConns.map((conn) => { + const cfg = conn.config as Record ?? {}; + return ( +
+
+

{conn.name}

+
+ {conn.type} + {cfg.baseUrl && {cfg.baseUrl}} + {cfg.authType && {cfg.authType}} + {conn.lastSyncAt && Testat: {new Date(conn.lastSyncAt).toLocaleDateString('ro-RO')}} +
+
+
+ + {conn.status} + + +
+
+ ); + })} +
+ )} + + ← Toate integrările +
+ ); +}