"use client"; import { useCallback, useMemo, useState } from "react"; import Link from "next/link"; import { AdminRulesPanel } from "@/components/admin-rules-panel"; import { Footer } from "@/components/footer"; import { useTenant } from "@/contexts/TenantContext"; import { TenantSelector } from "@/components/tenant-selector"; const BACKEND_BASE_URL = process.env.NEXT_PUBLIC_BACKEND_BASE_URL ?? "http://localhost:8000"; type StatusState = { tone: "info" | "success" | "error"; message: string } | null; export default function AdminRulesPage() { const { tenantId } = useTenant(); const [rulesInput, setRulesInput] = useState(""); const [deleteInput, setDeleteInput] = useState(""); const [rules, setRules] = useState([]); const [loading, setLoading] = useState(false); const [status, setStatus] = useState(null); const headers = useMemo(() => { if (!tenantId.trim()) return undefined; return { "Content-Type": "application/json", "x-tenant-id": tenantId.trim(), }; }, [tenantId]); const requireTenant = useCallback(() => { if (!tenantId.trim()) { setStatus({ tone: "error", message: "Enter a tenant ID in the navbar first." }); return false; } return true; }, [tenantId]); const handleRefresh = useCallback(async () => { if (!requireTenant()) return; try { setLoading(true); setStatus({ tone: "info", message: "Loading rules..." }); const response = await fetch(`${BACKEND_BASE_URL}/admin/rules`, { method: "GET", headers, }); if (!response.ok) { throw new Error(`Backend error ${response.status}`); } const data = await response.json(); setRules(data.rules ?? []); setStatus({ tone: "success", message: "Rules synced." }); } catch (error: any) { setStatus({ tone: "error", message: error.message || "Failed to fetch rules" }); } finally { setLoading(false); } }, [headers, requireTenant]); const handleUpload = useCallback(async () => { if (!requireTenant()) return; const lines = rulesInput .split("\n") .map((line) => line.trim()) .filter(Boolean); if (!lines.length) { setStatus({ tone: "error", message: "Add at least one rule to upload." }); return; } try { setLoading(true); setStatus({ tone: "info", message: "Uploading rules..." }); const response = await fetch(`${BACKEND_BASE_URL}/admin/rules/bulk`, { method: "POST", headers, body: JSON.stringify({ rules: lines }), }); if (!response.ok) { const details = await response.text(); throw new Error(details || `Backend error ${response.status}`); } await handleRefresh(); setRulesInput(""); setStatus({ tone: "success", message: "Rules uploaded successfully." }); } catch (error: any) { setStatus({ tone: "error", message: error.message || "Failed to upload rules" }); } finally { setLoading(false); } }, [handleRefresh, headers, requireTenant, rulesInput]); const handleDelete = useCallback(async () => { if (!requireTenant()) return; if (!deleteInput.trim()) { setStatus({ tone: "error", message: "Enter the rule text you want to delete." }); return; } try { setLoading(true); setStatus({ tone: "info", message: "Deleting rule..." }); const response = await fetch( `${BACKEND_BASE_URL}/admin/rules/${encodeURIComponent(deleteInput.trim())}`, { method: "DELETE", headers, } ); if (!response.ok) { const details = await response.text(); throw new Error(details || `Backend error ${response.status}`); } await handleRefresh(); setDeleteInput(""); setStatus({ tone: "success", message: "Rule deleted." }); } catch (error: any) { setStatus({ tone: "error", message: error.message || "Failed to delete rule" }); } finally { setLoading(false); } }, [deleteInput, handleRefresh, headers, requireTenant]); return (
IC IntegraChat · Admin Rule Ingestion
← Back Home

Push governance policies, compliance workflows, and red-flag patterns to the backend's persistent rules store using the MCP admin services.