import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import FileUploader from '~/components/file-uploader' import StyledButton from '~/components/StyledButton' import StyledSectionHeader from '~/components/StyledSectionHeader' import StyledTable from '~/components/StyledTable' import { useNotifications } from '~/context/NotificationContext' import api from '~/lib/api' import { IconX } from '@tabler/icons-react' import { useModals } from '~/context/ModalContext' import StyledModal from '../StyledModal' import ActiveEmbedJobs from '~/components/ActiveEmbedJobs' interface KnowledgeBaseModalProps { aiAssistantName?: string onClose: () => void } function sourceToDisplayName(source: string): string { const parts = source.split(/[/\\]/) return parts[parts.length - 1] } export default function KnowledgeBaseModal({ aiAssistantName = "AI Assistant", onClose }: KnowledgeBaseModalProps) { const { t } = useTranslation() const { addNotification } = useNotifications() const [files, setFiles] = useState([]) const [confirmDeleteSource, setConfirmDeleteSource] = useState(null) const fileUploaderRef = useRef>(null) const { openModal, closeModal } = useModals() const queryClient = useQueryClient() const { data: storedFiles = [], isLoading: isLoadingFiles } = useQuery({ queryKey: ['storedFiles'], queryFn: () => api.getStoredRAGFiles(), select: (data) => data || [], }) const uploadMutation = useMutation({ mutationFn: (file: File) => api.uploadDocument(file), onSuccess: (data) => { addNotification({ type: 'success', message: data?.message || t('chat.uploadSuccess'), }) setFiles([]) if (fileUploaderRef.current) { fileUploaderRef.current.clear() } }, onError: (error: any) => { addNotification({ type: 'error', message: error?.message || t('chat.uploadFailed'), }) }, }) const deleteMutation = useMutation({ mutationFn: (source: string) => api.deleteRAGFile(source), onSuccess: () => { addNotification({ type: 'success', message: t('chat.fileRemoved') }) setConfirmDeleteSource(null) queryClient.invalidateQueries({ queryKey: ['storedFiles'] }) }, onError: (error: any) => { addNotification({ type: 'error', message: error?.message || t('chat.fileDeleteFailed') }) setConfirmDeleteSource(null) }, }) const syncMutation = useMutation({ mutationFn: () => api.syncRAGStorage(), onSuccess: (data) => { addNotification({ type: 'success', message: data?.message || t('chat.syncSuccess'), }) }, onError: (error: any) => { addNotification({ type: 'error', message: error?.message || t('chat.syncFailed'), }) }, }) const handleUpload = () => { if (files.length > 0) { uploadMutation.mutate(files[0]) } } const handleConfirmSync = () => { openModal( { syncMutation.mutate() closeModal( "confirm-sync-modal" ) }} onCancel={() => closeModal("confirm-sync-modal")} open={true} confirmText={t('chat.confirmSyncButton')} confirmVariant='primary' >

{t('chat.syncDescription')}

, "confirm-sync-modal" ) } return (

{t('chat.knowledgeBaseTitle')}

{ setFiles(Array.from(uploadedFiles)) }} />
{t('common.upload')}

{t('chat.whyUpload')}

1

{t('chat.kbIntegrationTitle', { name: aiAssistantName })}

{t('chat.kbIntegrationDescription', { name: aiAssistantName })}

2

{t('chat.ocrTitle')}

{t('chat.ocrDescription')}

3

{t('chat.libraryTitle')}

{t('chat.libraryDescription', { name: aiAssistantName })}

{t('chat.syncStorage')}
className="font-semibold" rowLines={true} columns={[ { accessor: 'source', title: t('chat.fileName'), render(record) { return {sourceToDisplayName(record.source)} }, }, { accessor: 'source', title: '', render(record) { const isConfirming = confirmDeleteSource === record.source const isDeleting = deleteMutation.isPending && confirmDeleteSource === record.source if (isConfirming) { return (
{t('chat.removeFromKb')} deleteMutation.mutate(record.source)} disabled={isDeleting} > {isDeleting ? t('chat.deleting') : t('common.confirm')} setConfirmDeleteSource(null)} disabled={isDeleting} > {t('common.cancel')}
) } return (
setConfirmDeleteSource(record.source)} disabled={deleteMutation.isPending} loading={deleteMutation.isPending && confirmDeleteSource === record.source} >{t('common.delete')}
) }, }, ]} data={storedFiles.map((source) => ({ source }))} loading={isLoadingFiles} />
) }