Files
aurak/web/components/ChunkInfoDrawer.tsx
Developer 0a9588abb7 feat: implement QuestionBank CRUD with pagination and template query
- Add pagination support to findAll (page, limit query params)
- Add findByTemplateId method to service
- Add GET /by-template/:templateId endpoint to controller
- Service already includes CRUD for QuestionBank and QuestionBankItem
2026-04-23 17:19:11 +08:00

150 lines
6.2 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { X } from 'lucide-react';
import { knowledgeBaseService } from '../services/knowledgeBaseService';
import { useLanguage } from '../contexts/LanguageContext';
interface ChunkInfo {
fileId: string;
fileName: string;
totalChunks: number;
chunkSize: number;
chunkOverlap: number;
chunks: Array<{
index: number;
content: string;
contentLength: number;
startPosition: number;
endPosition: number;
}>;
}
interface ChunkInfoDrawerProps {
isOpen: boolean;
onClose: () => void;
fileId: string;
fileName: string;
authToken: string;
}
export const ChunkInfoDrawer: React.FC<ChunkInfoDrawerProps> = ({
isOpen,
onClose,
fileId,
fileName,
authToken,
}) => {
const { t } = useLanguage();
const [chunkInfo, setChunkInfo] = useState<ChunkInfo | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (isOpen && fileId) {
loadChunks();
}
}, [isOpen, fileId]);
const loadChunks = async () => {
setLoading(true);
setError(null);
try {
const data = await knowledgeBaseService.getFileChunks(fileId, authToken);
setChunkInfo(data);
} catch (err) {
console.error('Failed to load chunks:', err);
setError(t('errorLoadData'));
} finally {
setLoading(false);
}
};
if (!isOpen) return null;
return (
<>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50 z-[9998]"
onClick={onClose}
/>
{/* Drawer */}
<div className="fixed right-0 top-0 h-full w-full md:w-2/3 lg:w-1/2 bg-white shadow-2xl z-[9999] flex flex-col">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-slate-200 shrink-0">
<div>
<h2 className="text-xl font-semibold text-slate-800">
{t('chunkInfo')}
</h2>
<p className="text-sm text-slate-500 mt-1">{fileName}</p>
</div>
<button
onClick={onClose}
className="p-2 hover:bg-slate-100 rounded-lg transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-6">
{loading ? (
<div className="flex items-center justify-center h-full">
<div className="text-slate-500">{t('loading')}</div>
</div>
) : error ? (
<div className="flex items-center justify-center h-full">
<div className="text-red-500">{error}</div>
</div>
) : chunkInfo ? (
<div>
{/* Summary */}
<div className="bg-slate-50 rounded-lg p-4 mb-6 space-y-2">
<div className="flex justify-between">
<span className="text-slate-600">{t('totalChunks')}:</span>
<span className="font-semibold">{chunkInfo.totalChunks}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-600">{t('chunkSize')}:</span>
<span className="font-semibold">{chunkInfo.chunkSize} tokens</span>
</div>
<div className="flex justify-between">
<span className="text-slate-600">{t('chunkOverlap')}:</span>
<span className="font-semibold">{chunkInfo.chunkOverlap} tokens</span>
</div>
</div>
{/* Chunks List */}
<div className="space-y-4">
{chunkInfo.chunks.map((chunk) => (
<div
key={chunk.index}
className="border border-slate-200 rounded-lg p-4 hover:border-blue-300 transition-colors"
>
<div className="flex justify-between items-center mb-3">
<span className="font-semibold text-slate-800">
{t('chunkIndex')} #{chunk.index}
</span>
<span className="text-sm text-slate-500">
{chunk.contentLength} {t('contentLength')}
</span>
</div>
<div className="bg-white border border-slate-200 rounded-lg p-4 max-h-96 overflow-y-auto">
<div className="text-slate-700 text-sm leading-7 whitespace-pre-wrap break-words" style={{ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif' }}>
{chunk.content}
</div>
</div>
<div className="text-xs text-slate-400 mt-2">
{t('position')}: {chunk.startPosition} - {chunk.endPosition}
</div>
</div>
))}
</div>
</div>
) : null}
</div>
</div>
</>
);
};