import React, { useState } from 'react'; import { View, Text, StyleSheet, FlatList, TouchableOpacity, Alert, } from 'react-native'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { COLORS, FONT_SIZE, SPACING, RADIUS } from '../../../theme'; import { useTheme } from '../../../theme'; import { Card, StatusBadge, Spinner, EmptyState, Button, Input } from '../../../components/ui'; import { useToast } from '../../../contexts/ToastContext'; import { documentsAPI, urlSourcesAPI } from '../../../services/api'; import { Document, URLSource } from '../../../types'; interface Props { chatbotId: string; } export function DocumentsTab({ chatbotId }: Props) { const { theme } = useTheme(); const toast = useToast(); const qc = useQueryClient(); const [urlInput, setUrlInput] = useState(''); const [addingUrl, setAddingUrl] = useState(false); const [showUrlInput, setShowUrlInput] = useState(false); const { data: documents, isLoading: loadingDocs } = useQuery({ queryKey: ['documents', chatbotId], queryFn: () => documentsAPI.list(chatbotId), }); const { data: urlSources } = useQuery({ queryKey: ['urlSources', chatbotId], queryFn: () => urlSourcesAPI.list(chatbotId), }); const deleteMutation = useMutation({ mutationFn: (docId: string) => documentsAPI.delete(chatbotId, docId), onSuccess: () => { qc.invalidateQueries({ queryKey: ['documents', chatbotId] }); toast.success('Document deleted'); }, onError: () => toast.error('Failed to delete document'), }); const retryMutation = useMutation({ mutationFn: (docId: string) => documentsAPI.retry(chatbotId, docId), onSuccess: () => { qc.invalidateQueries({ queryKey: ['documents', chatbotId] }); toast.success('Retry started'); }, }); const deleteUrlMutation = useMutation({ mutationFn: (sourceId: string) => urlSourcesAPI.delete(chatbotId, sourceId), onSuccess: () => { qc.invalidateQueries({ queryKey: ['urlSources', chatbotId] }); toast.success('URL source removed'); }, }); const handleAddUrl = async () => { const url = urlInput.trim(); if (!url) return; if (!url.startsWith('http://') && !url.startsWith('https://')) { toast.error('Enter a valid URL starting with http:// or https://'); return; } setAddingUrl(true); try { await urlSourcesAPI.add(chatbotId, url); qc.invalidateQueries({ queryKey: ['urlSources', chatbotId] }); setUrlInput(''); setShowUrlInput(false); toast.success('URL added! Scraping in background...'); } catch (err: any) { toast.error(err?.response?.data?.detail ?? 'Failed to add URL'); } finally { setAddingUrl(false); } }; const confirmDelete = (doc: Document) => { Alert.alert('Delete Document', `Remove "${doc.file_name}"?`, [ { text: 'Cancel', style: 'cancel' }, { text: 'Delete', style: 'destructive', onPress: () => deleteMutation.mutate(doc.id) }, ]); }; const formatSize = (bytes: number) => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }; type ListItem = | { type: 'doc'; data: Document } | { type: 'url'; data: URLSource } | { type: 'upload-tip' }; const listData: ListItem[] = [ { type: 'upload-tip' }, ...(documents ?? []).map((d: Document) => ({ type: 'doc' as const, data: d })), ...(urlSources ?? []).map((u: URLSource) => ({ type: 'url' as const, data: u })), ]; return ( {/* Add URL bar */}