import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { apiFetch } from '@/lib/http' import { useUIStore } from '@/stores/uiStore' import { cn } from '@/lib/utils' import { LoadingSkeleton } from '@/components/common/LoadingSkeleton' interface DataSource { id: string type: 'jira' | 'confluence' | 'github' | 'slack' name: string url: string enabled: boolean last_sync: string | null sync_status: 'idle' | 'syncing' | 'error' error_msg: string | null } async function fetchSources(): Promise<{ sources: DataSource[] }> { const res = await apiFetch('/api/admin/data-sources') return res.json() } async function toggleSource(id: string, enabled: boolean): Promise { await apiFetch(`/api/admin/data-sources/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled }), }) } const TYPE_ICONS: Record = { jira: 'πŸ”·', confluence: 'πŸ“˜', github: 'πŸ™', slack: 'πŸ’¬', } const SYNC_STATUS_STYLES: Record = { idle: 'text-stone-400', syncing: 'text-blue-500', error: 'text-red-500', } export function DataSourceManager() { const qc = useQueryClient() const addToast = useUIStore((s) => s.addToast) const [adding, setAdding] = useState(false) const { data, isLoading } = useQuery({ queryKey: ['admin-data-sources'], queryFn: fetchSources, staleTime: 60_000, }) const toggle = useMutation({ mutationFn: ({ id, enabled }: { id: string; enabled: boolean }) => toggleSource(id, enabled), onSuccess: () => qc.invalidateQueries({ queryKey: ['admin-data-sources'] }), onError: () => addToast({ type: 'error', message: 'Failed to update data source' }), }) return (

Connected Data Sources

{isLoading ? (
) : (data?.sources ?? []).length === 0 ? (

No data sources configured.

) : (
{(data?.sources ?? []).map((src) => (
{TYPE_ICONS[src.type]}

{src.name}

{src.url}

{src.sync_status === 'error' && src.error_msg && (

{src.error_msg}

)}

{src.sync_status === 'syncing' ? 'Syncing…' : src.last_sync ? `Last sync: ${new Date(src.last_sync).toLocaleDateString()}` : 'Never synced'}

{/* Toggle */}
))}
)} {/* Add source placeholder modal hint */} {adding && (

Data source wizard β€” configure integration credentials in .env and register via the API.

See POST /api/admin/data-sources in the API docs.

)}
) }