/** * Configuration tab — extracted from Settings.tsx (cq-002) */ import React, { useState } from 'react'; import type { StorageStats } from '@agentkitai/agentlens-core'; import { getStats, getConfig, updateConfig, type ConfigUpdate, } from '../../api/client'; import { useApi } from '../../hooks/useApi'; // ─── Helpers ──────────────────────────────────────────────── function StatsCard({ label, value }: { label: string; value: string }): React.ReactElement { return (

{label}

{value}

); } function formatNumber(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; return n.toLocaleString(); } // ─── Configuration Tab (Story 8.4) ───────────────────────── interface ConfigFormState { retentionDays: string; agentGateUrl: string; agentGateSecret: string; formBridgeUrl: string; formBridgeSecret: string; } export function ConfigTab(): React.ReactElement { const { data: stats, loading: statsLoading, error: statsError } = useApi(() => getStats(), []); const { data: configData, loading: configLoading, error: configError, refetch: refetchConfig } = useApi(() => getConfig(), []); const [editing, setEditing] = useState(false); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(null); const [saveSuccess, setSaveSuccess] = useState(false); const [form, setForm] = useState({ retentionDays: '90', agentGateUrl: '', agentGateSecret: '', formBridgeUrl: '', formBridgeSecret: '', }); // Sync form state when config loads React.useEffect(() => { if (configData) { setForm({ retentionDays: String(configData.retentionDays ?? 90), agentGateUrl: configData.agentGateUrl ?? '', agentGateSecret: '', formBridgeUrl: configData.formBridgeUrl ?? '', formBridgeSecret: '', }); } }, [configData]); const handleEdit = () => { setEditing(true); setSaveSuccess(false); setSaveError(null); }; const handleCancel = () => { setEditing(false); setSaveError(null); if (configData) { setForm({ retentionDays: String(configData.retentionDays ?? 90), agentGateUrl: configData.agentGateUrl ?? '', agentGateSecret: '', formBridgeUrl: configData.formBridgeUrl ?? '', formBridgeSecret: '', }); } }; const handleSave = async () => { setSaving(true); setSaveError(null); setSaveSuccess(false); try { const payload: ConfigUpdate = { retentionDays: parseInt(form.retentionDays, 10) || 90, agentGateUrl: form.agentGateUrl, formBridgeUrl: form.formBridgeUrl, }; if (form.agentGateSecret) { payload.agentGateSecret = form.agentGateSecret; } if (form.formBridgeSecret) { payload.formBridgeSecret = form.formBridgeSecret; } await updateConfig(payload); setSaveSuccess(true); setEditing(false); refetchConfig(); } catch (err) { setSaveError(err instanceof Error ? err.message : String(err)); } finally { setSaving(false); } }; const updateField = (field: keyof ConfigFormState, value: string) => { setForm((f) => ({ ...f, [field]: value })); }; return (
{/* Storage Stats */}

Storage Statistics

{statsError && (
{statsError}
)} {statsLoading && !stats && (

Loading…

)} {stats && (
)}
{/* Configuration */}

Configuration

{!editing && ( )}
{configError && (
{configError}
)} {configLoading && !configData && (

Loading configuration…

)} {saveSuccess && (
Configuration saved successfully.
)} {saveError && (
Failed to save: {saveError}
)}
{/* Retention Period */}

Retention Period

Events older than this are automatically deleted

{editing ? (
updateField('retentionDays', e.target.value)} className="w-20 rounded border border-gray-300 px-2 py-1 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" /> days
) : ( {configData?.retentionDays ?? 90} days )}
{/* AgentGate URL */}

AgentGate URL

Base URL of your AgentGate instance (approval events arrive via the inbound webhook — see the Integrations tab)

{editing ? ( updateField('agentGateUrl', e.target.value)} placeholder="https://..." className="ml-4 w-64 rounded border border-gray-300 px-2 py-1 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" /> ) : ( {configData?.agentGateUrl || 'Not configured'} )}
{/* AgentGate Secret */}

AgentGate Secret

Shared secret for AgentGate webhook verification

{editing ? ( updateField('agentGateSecret', e.target.value)} placeholder="Leave blank to keep current" className="ml-4 w-64 rounded border border-gray-300 px-2 py-1 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" /> ) : ( {configData?.agentGateSecretSet ? 'Configured' : 'Not set'} )}
{/* FormBridge URL */}

FormBridge URL

Base URL of your FormBridge instance (form events arrive via the inbound webhook — see the Integrations tab)

{editing ? ( updateField('formBridgeUrl', e.target.value)} placeholder="https://..." className="ml-4 w-64 rounded border border-gray-300 px-2 py-1 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" /> ) : ( {configData?.formBridgeUrl || 'Not configured'} )}
{/* FormBridge Secret */}

FormBridge Secret

Shared secret for FormBridge webhook verification

{editing ? ( updateField('formBridgeSecret', e.target.value)} placeholder="Leave blank to keep current" className="ml-4 w-64 rounded border border-gray-300 px-2 py-1 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" /> ) : ( {configData?.formBridgeSecretSet ? 'Configured' : 'Not set'} )}
{/* Save/Cancel buttons */} {editing && (
)}
); }