import { useEffect, useMemo, useState, type ReactElement } from "react"; import { useNavigate } from "react-router-dom"; import { Plus, Copy, History, Trash2, Edit3, ClipboardCheck, Play, Activity, } from "lucide-react"; import { Layout } from "@/components/layout/Layout"; import { DataTable, type Column, Pagination, PrimaryButton, ActionDropdown, DeleteConfirmationModal, SearchBox, FilterDropdown, } from "@/components/shared"; import { aiService } from "@/services/ai-service"; import { moduleService } from "@/services/module-service"; import type { AIPrompt } from "@/types/ai"; import { showToast } from "@/utils/toast"; import { formatDate } from "@/utils/format-date"; import { PromptVersionsModal } from "@/components/tenant/PromptVersionsModal"; import { cn } from "@/lib/utils"; const PromptManagement = (): ReactElement => { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [prompts, setPrompts] = useState([]); const [search, setSearch] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); const [page, setPage] = useState(1); const [limit, setLimit] = useState(10); const [pagination, setPagination] = useState({ page: 1, limit: 10, total: 0, totalPages: 1, }); // Module filter states const [modules, setModules] = useState>([]); const [selectedModuleId, setSelectedModuleId] = useState(""); // Modal states const [selectedPrompt, setSelectedPrompt] = useState(null); const [isVersionsOpen, setIsVersionsOpen] = useState(false); const [isDeleteOpen, setIsDeleteOpen] = useState(false); const [isActionLoading, setIsActionLoading] = useState(false); useEffect(() => { const timer = setTimeout(() => { setDebouncedSearch(search); setPage(1); }, 500); return () => clearTimeout(timer); }, [search]); // Load modules list on mount useEffect(() => { const loadModules = async (): Promise => { try { const res = await moduleService.getMyModules(); setModules( (res.data || []).map((m: any) => ({ value: m.id, label: m.name, })) ); } catch (err: any) { console.error("Failed to load modules list", err); } }; void loadModules(); }, []); const loadPrompts = async (): Promise => { setIsLoading(true); setError(null); try { const result = await aiService.listPrompts({ page, limit, search: debouncedSearch || undefined, module_id: selectedModuleId || undefined, }); setPrompts(result.data || []); setPagination({ page: result.pagination?.page || page, limit: result.pagination?.limit || limit, total: result.pagination?.total || 0, totalPages: result.pagination?.totalPages || 1, }); } catch (err: unknown) { const message = (err as { response?: { data?: { error?: { message?: string } } } }) ?.response?.data?.error?.message || "Failed to load prompts"; setError(message); showToast.error(message); } finally { setIsLoading(false); } }; useEffect(() => { void loadPrompts(); }, [page, limit, debouncedSearch, selectedModuleId]); const handleStatusToggle = async (prompt: AIPrompt) => { const newStatus = prompt.status === "active" ? "draft" : "active"; try { await aiService.updatePromptStatus(prompt.id, newStatus); showToast.success(`Prompt marked as ${newStatus}`); void loadPrompts(); } catch (err: any) { showToast.error("Failed to update status"); } }; const handleDelete = async () => { if (!selectedPrompt) return; setIsActionLoading(true); try { await aiService.deletePrompt(selectedPrompt.id); showToast.success("Prompt deleted successfully"); setIsDeleteOpen(false); void loadPrompts(); } catch (err: any) { showToast.error("Failed to delete prompt"); } finally { setIsActionLoading(false); } }; const handleClone = async (prompt: AIPrompt) => { try { await aiService.clonePrompt(prompt.id); // Assuming this exists or I'll add it showToast.success("Prompt cloned successfully"); void loadPrompts(); } catch (err: any) { showToast.error("Failed to clone prompt"); } }; const columns: Column[] = useMemo( () => [ { key: "name", label: "Name & Description", render: (row) => (

navigate(`/tenant/ai/prompts/${row.id}/edit`)} > {row.name}

{row.description || "No description"}

), }, { key: "useCase", label: "Use Case", render: (row) => { if (!row.useCase) return ; return ( {row.useCase} ); }, }, { key: "status", label: "Status", render: (row) => (
handleStatusToggle(row)} >
{row.status || "draft"}
), }, { key: "version", label: "Version", render: (row) => ( v{row.version || 1} ), }, { key: "tags", label: "Tags", render: (row) => { const tags = ((row as any).tags || []) as string[]; if (!tags.length) { return ; } return (
{tags.slice(0, 2).map((tag, index) => ( {tag} ))} {tags.length > 2 && ( +{tags.length - 2} )}
); }, }, { key: "Updated", label: "Last Updated", render: (row) => ( {formatDate(row.updatedAt || row.createdAt || "")} ), }, { key: "actions", label: "", align: "right", render: (row) => (
, label: "Prompt Test Cases", onClick: () => navigate(`/tenant/ai/prompts/${row.id}/test-cases`), }, { icon: , label: "Edit Prompt", onClick: () => navigate(`/tenant/ai/prompts/${row.id}/edit`), }, { icon: , label: "Version History", onClick: () => { setSelectedPrompt(row); setIsVersionsOpen(true); }, }, { icon: , label: "Run / Execute", onClick: () => navigate(`/tenant/ai/prompts/${row.id}/execute`), }, { icon: , label: "Execution Logs", onClick: () => navigate(`/tenant/ai/prompts/${row.id}/executions`), }, { icon: , label: "Clone Prompt", onClick: () => handleClone(row), }, { icon: , label: "Delete Prompt", variant: "danger", onClick: () => { setSelectedPrompt(row); setIsDeleteOpen(true); }, }, ]} />
), }, ], [navigate, loadPrompts], ); return ( navigate("/tenant/ai/prompts/create")} className="flex items-center gap-2 h-10 shadow-sm" > Create Prompt ), }} >
{/*
*/} { setSelectedModuleId(val ? (Array.isArray(val) ? val[0] : val) : ""); setPage(1); }} options={modules.map((m) => ({ value: m.value, label: m.label }))} placeholder="All Modules" /> {/*
*/}
item.id} isLoading={isLoading} error={error} emptyMessage="No prompts found. Click 'Create Prompt' to get started." /> {pagination.total > 0 && (
{ setLimit(newLimit); setPage(1); }} />
)}
setIsVersionsOpen(false)} prompt={selectedPrompt} onRollbackSuccess={loadPrompts} /> setIsDeleteOpen(false)} onConfirm={handleDelete} isLoading={isActionLoading} title="Delete Prompt" message="Are you sure you want to delete this prompt" itemName={selectedPrompt?.name} />
); }; export default PromptManagement;