// (c) 2025 TWWIM UG. All rights reserved. import { useState } from 'react'; import { FileText, Trash2, Plus, Search, X, Upload, AlertCircle, Database, ShieldAlert } from 'lucide-react'; import { useKnowledgeEntries, useKnowledgeStats, useCreateKnowledgeEntry, useDeleteKnowledgeEntry } from '../hooks'; import { useTranslation } from '@/i18n/TranslationProvider'; import { tokenStorage } from '@/infrastructure/storage/LocalTokenStorage'; import type { KnowledgeEntry } from '@/infrastructure/http/api/knowledge'; const MAX_CONTENT_BYTES = 2 * 1024 * 1024; // 2 MB const TYPE_COLORS: Record = { PRODUCT: { bg: 'bg-blue-100', text: 'text-blue-800' }, PAGE: { bg: 'bg-green-100', text: 'text-green-800' }, DOCUMENT: { bg: 'bg-purple-100', text: 'text-purple-800' }, FAQ: { bg: 'bg-amber-100', text: 'text-amber-800' }, CUSTOM: { bg: 'bg-gray-100', text: 'text-gray-800' }, }; function contentByteSize(text: string): number { return new Blob([text]).size; } function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; } function TypeBadge({ type }: { type: string }) { const colors = TYPE_COLORS[type] || TYPE_COLORS.CUSTOM; return ( {type} ); } function UsageBar({ used, max }: { used: number; max: number }) { const pct = max > 0 ? Math.min((used / max) * 100, 100) : 0; const color = pct >= 100 ? 'bg-red-500' : pct >= 80 ? 'bg-amber-500' : 'bg-cyan-500'; return (
= 100 ? 'text-red-600' : 'text-gray-700'}`}> {used}/{max}
); } function AddEntryForm({ tenantId, onClose, }: { tenantId: string; onClose: () => void; }) { const { t } = useTranslation(); const createMutation = useCreateKnowledgeEntry(tenantId); const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const [type, setType] = useState<'FAQ' | 'PRODUCT' | 'PAGE' | 'DOCUMENT' | 'CUSTOM'>('FAQ'); const [url, setUrl] = useState(''); const [category, setCategory] = useState(''); const contentBytes = contentByteSize(content); const oversized = contentBytes > MAX_CONTENT_BYTES; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!title.trim() || !content.trim() || oversized) return; createMutation.mutate( { type, title: title.trim(), content: content.trim(), ...(url.trim() ? { url: url.trim() } : {}), ...(category.trim() ? { category: category.trim() } : {}), }, { onSuccess: () => { onClose(); }, }, ); }; return (

{t('knowledge.addEntry')}

setCategory(e.target.value)} placeholder={t('common.optional')} className="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-cyan-500 focus:outline-none" />
setTitle(e.target.value)} placeholder={t('knowledge.titlePlaceholder')} className="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-cyan-500 focus:outline-none" required />
{formatBytes(contentBytes)} / {formatBytes(MAX_CONTENT_BYTES)}