diff --git a/frontend/src/components/collections/LightweightCollectionDetail.tsx b/frontend/src/components/collections/LightweightCollectionDetail.tsx index f10c0cc7..fb8e4367 100644 --- a/frontend/src/components/collections/LightweightCollectionDetail.tsx +++ b/frontend/src/components/collections/LightweightCollectionDetail.tsx @@ -22,6 +22,7 @@ import { useNotification } from '../../contexts/NotificationContext'; // Import the API client and types import apiClient, { Collection, CollectionDocument } from '../../services/apiClient'; import PodcastGenerationModal from '../podcasts/PodcastGenerationModal'; +import SuggestedQuestions from './SuggestedQuestions'; // Use CollectionDocument type from apiClient instead of local CollectionFile type CollectionFile = CollectionDocument; @@ -242,6 +243,22 @@ const LightweightCollectionDetail: React.FC = () => { setFilesToUpload([]); }; + const handleSuggestedQuestionClick = (question: string) => { + // Navigate to RAG search page with the collection and question + if (collection?.status === 'ready' || collection?.status === 'completed') { + navigate('/search', { + state: { + collectionId: collection.id, + collectionName: collection.name, + collectionDescription: collection.description, + initialQuery: question + } + }); + } else { + addNotification('warning', 'Collection Not Ready', 'This collection is not ready for searching yet.'); + } + }; + const filteredDocuments = collection?.documents.filter(doc => doc.name.toLowerCase().includes(searchQuery.toLowerCase()) ) || []; @@ -345,6 +362,14 @@ const LightweightCollectionDetail: React.FC = () => { + {/* Suggested Questions */} +
+ +
+ {/* Documents Table */}
diff --git a/frontend/src/components/collections/SuggestedQuestions.tsx b/frontend/src/components/collections/SuggestedQuestions.tsx new file mode 100644 index 00000000..a3966925 --- /dev/null +++ b/frontend/src/components/collections/SuggestedQuestions.tsx @@ -0,0 +1,151 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import apiClient, { SuggestedQuestion } from '../../services/apiClient'; +import { useNotification } from '../../contexts/NotificationContext'; +import { LightBulbIcon, ArrowPathIcon } from '@heroicons/react/24/outline'; + +interface SuggestedQuestionsProps { + collectionId: string; + onQuestionClick: (question: string) => void; +} + +const SuggestedQuestions: React.FC = ({ collectionId, onQuestionClick }) => { + const [questions, setQuestions] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [isRefreshing, setIsRefreshing] = useState(false); + const [error, setError] = useState(null); + const { addNotification } = useNotification(); + + + useEffect(() => { + let isMounted = true; + + const fetchQuestionsWithCleanup = async () => { + if (!isMounted) return; + + setIsLoading(true); + setError(null); + try { + const fetchedQuestions = await apiClient.getSuggestedQuestions(collectionId); + if (isMounted) { + setQuestions(fetchedQuestions); + } + } catch (err) { + if (isMounted) { + const errorMessage = err instanceof Error ? err.message : 'Failed to load suggested questions.'; + console.error('Error fetching suggested questions:', err); + setError(errorMessage); + addNotification('error', 'Error', `Could not fetch suggested questions: ${errorMessage}`); + } + } finally { + if (isMounted) { + setIsLoading(false); + } + } + }; + + fetchQuestionsWithCleanup(); + + return () => { + isMounted = false; + }; + }, [collectionId, addNotification]); + + const handleRefresh = async () => { + setIsRefreshing(true); + try { + const fetchedQuestions = await apiClient.getSuggestedQuestions(collectionId); + setQuestions(fetchedQuestions); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : 'Failed to load suggested questions.'; + console.error('Error fetching suggested questions:', err); + setError(errorMessage); + addNotification('error', 'Error', `Could not fetch suggested questions: ${errorMessage}`); + } finally { + setIsRefreshing(false); + } + }; + + if (isLoading) { + return ( +
+
+
+
+
+
+
+
+ ); + } + + if (error) { + return ( +
+

{error}

+ +
+ ); + } + + if (questions.length === 0) { + return ( +
+
+
+ + Suggested Questions +
+ +
+

No suggested questions available at the moment. Questions will be generated automatically after document processing is complete.

+
+ ); + } + + return ( +
+
+
+ + Suggested Questions +
+ +
+
+ {questions.map((q) => ( + + ))} +
+
+ ); +}; + +export default SuggestedQuestions; diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts index cf1b42f2..e33ab866 100644 --- a/frontend/src/services/apiClient.ts +++ b/frontend/src/services/apiClient.ts @@ -260,6 +260,13 @@ interface PodcastQuestionInjection { user_id: string; } +interface SuggestedQuestion { + id: string; + collection_id: string; + question: string; + created_at: string; +} + class ApiClient { private client: AxiosInstance; @@ -403,6 +410,13 @@ class ApiClient { await this.client.delete(`/api/collections/${id}`); } + async getSuggestedQuestions(collectionId: string): Promise { + const response: AxiosResponse = await this.client.get( + `/api/collections/${collectionId}/questions` + ); + return response.data; + } + // Document API async uploadDocuments(collectionId: string, files: File[]): Promise { const formData = new FormData(); @@ -921,5 +935,6 @@ export type { PodcastQuestionInjection, VoiceSettings, PodcastStepDetails, + SuggestedQuestion, VoiceId, };