'use client';
import React, { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import {
Plus,
Edit2,
Trash2,
Clock,
MoreVertical,
AlertCircle,
FileText,
Eye,
EyeOff,
Globe,
Search,
Loader2,
} from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
useKnowledgeBaseEntries,
useCreateKnowledgeBaseEntry,
useUpdateKnowledgeBaseEntry,
useDeleteKnowledgeBaseEntry,
} from '@/hooks/react-query/knowledge-base/use-knowledge-base-queries';
import { cn } from '@/lib/utils';
import { CreateKnowledgeBaseEntryRequest, KnowledgeBaseEntry, UpdateKnowledgeBaseEntryRequest } from '@/hooks/react-query/knowledge-base/types';
interface KnowledgeBaseManagerProps {
threadId: string;
}
interface EditDialogData {
entry?: KnowledgeBaseEntry;
isOpen: boolean;
}
const USAGE_CONTEXT_OPTIONS = [
{
value: 'always',
label: 'Always Active',
icon: Globe,
color: 'bg-green-50 text-green-700 border-green-200 dark:bg-green-900/20 dark:text-green-400 dark:border-green-800'
},
// {
// value: 'contextual',
// label: 'Smart Context',
// description: 'Included when contextually relevant',
// icon: Target,
// color: 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800'
// },
// {
// value: 'on_request',
// label: 'On Demand',
// description: 'Only when explicitly requested',
// icon: Zap,
// color: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/20 dark:text-amber-400 dark:border-amber-800'
// },
] as const;
const KnowledgeBaseSkeleton = () => (
{[1, 2, 3].map((i) => (
))}
);
export const KnowledgeBaseManager = ({ threadId }: KnowledgeBaseManagerProps) => {
const [editDialog, setEditDialog] = useState({ isOpen: false });
const [deleteEntryId, setDeleteEntryId] = useState(null);
const [searchQuery, setSearchQuery] = useState('');
const [formData, setFormData] = useState({
name: '',
description: '',
content: '',
usage_context: 'always',
});
const { data: knowledgeBase, isLoading, error } = useKnowledgeBaseEntries(threadId);
const createMutation = useCreateKnowledgeBaseEntry();
const updateMutation = useUpdateKnowledgeBaseEntry();
const deleteMutation = useDeleteKnowledgeBaseEntry();
const handleOpenCreateDialog = () => {
setFormData({
name: '',
description: '',
content: '',
usage_context: 'always',
});
setEditDialog({ isOpen: true });
};
const handleOpenEditDialog = (entry: KnowledgeBaseEntry) => {
setFormData({
name: entry.name,
description: entry.description || '',
content: entry.content,
usage_context: entry.usage_context,
});
setEditDialog({ entry, isOpen: true });
};
const handleCloseDialog = () => {
setEditDialog({ isOpen: false });
setFormData({
name: '',
description: '',
content: '',
usage_context: 'always',
});
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!formData.name.trim() || !formData.content.trim()) {
return;
}
try {
if (editDialog.entry) {
const updateData: UpdateKnowledgeBaseEntryRequest = {
name: formData.name !== editDialog.entry.name ? formData.name : undefined,
description: formData.description !== editDialog.entry.description ? formData.description : undefined,
content: formData.content !== editDialog.entry.content ? formData.content : undefined,
usage_context: formData.usage_context !== editDialog.entry.usage_context ? formData.usage_context : undefined,
};
const hasChanges = Object.values(updateData).some(value => value !== undefined);
if (hasChanges) {
await updateMutation.mutateAsync({ entryId: editDialog.entry.entry_id, data: updateData });
}
} else {
await createMutation.mutateAsync({ threadId, data: formData });
}
handleCloseDialog();
} catch (error) {
console.error('Error saving knowledge base entry:', error);
}
};
const handleDelete = async (entryId: string) => {
try {
await deleteMutation.mutateAsync(entryId);
setDeleteEntryId(null);
} catch (error) {
console.error('Error deleting knowledge base entry:', error);
}
};
const handleToggleActive = async (entry: KnowledgeBaseEntry) => {
try {
await updateMutation.mutateAsync({
entryId: entry.entry_id,
data: { is_active: !entry.is_active }
});
} catch (error) {
console.error('Error toggling entry status:', error);
}
};
const getUsageContextConfig = (context: string) => {
return USAGE_CONTEXT_OPTIONS.find(option => option.value === context) || USAGE_CONTEXT_OPTIONS[0];
};
if (isLoading) {
return ;
}
if (error) {
return (
Failed to load knowledge base
);
}
const entries = knowledgeBase?.entries || [];
const filteredEntries = entries.filter(entry =>
entry.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
entry.content.toLowerCase().includes(searchQuery.toLowerCase()) ||
(entry.description && entry.description.toLowerCase().includes(searchQuery.toLowerCase()))
);
return (
{entries.length > 0 && (
<>
{filteredEntries.length === 0 ? (
No entries match your search
) : (
filteredEntries.map((entry) => {
const contextConfig = getUsageContextConfig(entry.usage_context);
const ContextIcon = contextConfig.icon;
return (
{entry.name}
{!entry.is_active && (
Disabled
)}
{entry.description && (
{entry.description}
)}
{entry.content}
{contextConfig.label}
{new Date(entry.created_at).toLocaleDateString()}
{entry.content_tokens && (
~{entry.content_tokens.toLocaleString()} tokens
)}
handleOpenEditDialog(entry)}>
Edit
handleToggleActive(entry)}>
{entry.is_active ? (
<>
Disable
>
) : (
<>
Enable
>
)}
setDeleteEntryId(entry.entry_id)}
className="text-destructive focus:bg-destructive/10 focus:text-destructive"
>
Delete
);
})
)}
>
)}
{entries.length === 0 && (
No Knowledge Entries
Add knowledge entries to provide your agent with context, guidelines, and information it should always remember.
Create Your First Entry
)}
{editDialog.entry ? 'Edit Knowledge Entry' : 'Add Knowledge Entry'}
setDeleteEntryId(null)}>
Delete Knowledge Entry
This will permanently delete this knowledge entry. Your agent will no longer have access to this information.
Cancel
deleteEntryId && handleDelete(deleteEntryId)}
className="bg-destructive hover:bg-destructive/90"
disabled={deleteMutation.isPending}
>
{deleteMutation.isPending ? (
) : (
)}
Delete Entry
);
}