22import { useState , useEffect , useRef } from 'react' ;
33import { Card , CardContent , CardDescription , CardHeader , CardTitle } from '@/components/ui/card' ;
44import { Button } from '@/components/ui/button' ;
5- import { BookOpen , RotateCcw , ChevronLeft , ChevronRight , Shuffle , Eye , EyeOff , Loader2 } from 'lucide-react' ;
5+ import { BookOpen , RotateCcw , ChevronLeft , ChevronRight , Shuffle , Eye , EyeOff , Loader2 , XCircle } from 'lucide-react' ;
66import { apiService , FlashcardModel , DocumentStatus } from '../lib/api' ;
77
88interface FlashcardsTabProps {
@@ -32,12 +32,10 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
3232
3333 useEffect ( ( ) => {
3434 const newDocumentIds = documentIds . filter ( id => ! fetchedDocumentIds . current . has ( id ) ) ;
35- if ( newDocumentIds . length === 0 ) return ;
3635
37- // Mark as being processed to avoid duplicate processing
38- newDocumentIds . forEach ( id => processingDocumentIds . current . add ( id ) ) ;
36+ if ( newDocumentIds . length === 0 ) return ;
3937
40- // First, add pending entries for new documents
38+ // Immediately add pending entries for new documents to show loading state
4139 const pendingEntries = newDocumentIds . map ( documentId => ( {
4240 id : documentId ,
4341 title : 'Loading...' ,
@@ -46,32 +44,31 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
4644 source : 'Loading...' ,
4745 difficulty : 'Unknown' ,
4846 status : 'PENDING' as const ,
49- error : undefined ,
47+ error : 'Preparing flashcards...' ,
5048 flashcards : [ ]
5149 } ) ) ;
5250
53- setFlashcardDecks ( prevDecks => [ ...prevDecks , ...pendingEntries ] ) ;
51+ setFlashcardDecks ( prevDecks => {
52+ const existingDecks = prevDecks . filter ( d => ! newDocumentIds . includes ( d . id ) ) ;
53+ const newDecks = [ ...existingDecks , ...pendingEntries ] ;
54+ return newDecks ;
55+ } ) ;
5456
5557 const fetchFlashcards = async ( ) => {
5658 const flashcardPromises = newDocumentIds . map ( async ( documentId ) => {
5759 try {
58- // Check if flashcard data is already available from automatic processing
5960 const documentContent = await apiService . getDocumentContent ( documentId ) ;
6061
61- // If flashcards are ready and have data, use them directly
62+ // Simple status-based approach like SummaryTab
6263 if ( documentContent . flashcardStatus === 'PROCESSED' && documentContent . flashcardData ) {
63- try {
64- // The backend saves flashcard data directly as an array, not nested in response.flashcards
64+ // Parse flashcard data
6565 let flashcardsList = [ ] ;
6666
6767 if ( Array . isArray ( documentContent . flashcardData ) ) {
68- // Direct array of flashcards
6968 flashcardsList = documentContent . flashcardData ;
7069 } else if ( documentContent . flashcardData . response && Array . isArray ( documentContent . flashcardData . response . flashcards ) ) {
71- // Nested in response.flashcards
7270 flashcardsList = documentContent . flashcardData . response . flashcards ;
7371 } else if ( documentContent . flashcardData . flashcards && Array . isArray ( documentContent . flashcardData . flashcards ) ) {
74- // Nested in flashcards property
7572 flashcardsList = documentContent . flashcardData . flashcards ;
7673 }
7774
@@ -94,65 +91,32 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
9491 flashcards
9592 } ;
9693 }
97- } catch ( parseError ) {
98- console . warn ( 'Failed to parse existing flashcard data:' , parseError ) ;
99- }
10094 }
10195
102- // Check if we should wait for automatic processing or make individual API call
103- const shouldWaitForAutoProcessing =
104- documentContent . status === 'PROCESSING' ||
105- documentContent . summaryStatus === 'PROCESSING' ||
106- documentContent . quizStatus === 'PROCESSING' ||
107- documentContent . flashcardStatus === 'PROCESSING' ||
108- documentContent . summaryStatus === 'UPLOADED' ||
109- documentContent . flashcardStatus === 'UPLOADED' ;
110-
111- if ( shouldWaitForAutoProcessing ) {
112- return {
113- id : documentId ,
114- title : documentContent . originalName || 'Unknown Document' ,
115- description : 'Flashcards are being generated automatically...' ,
116- cardCount : 0 ,
117- source : documentContent . originalName || 'Unknown Document' ,
118- difficulty : 'Unknown' ,
119- status : 'PROCESSING' ,
120- error : 'Flashcards are being generated automatically...' ,
121- flashcards : [ ]
122- } ;
123- }
96+ // Return status based on flashcardStatus
97+ let status : 'PROCESSING' | 'PENDING' | 'ERROR' | 'PROCESSED' = 'PENDING' ;
98+ let errorMessage = undefined ;
12499
125- // Only make individual API call if automatic processing failed
126- if ( documentContent . flashcardStatus === 'ERROR' ) {
127- const res = await apiService . getFlashcardsForDocument ( documentId ) ;
128-
129- if ( res . status === 'PROCESSING' ) {
130- pollFlashcardStatus ( documentId ) ;
131- }
132-
133- return {
134- id : documentId ,
135- title : res . documentName || 'Unknown Document' ,
136- description : res . status === 'PROCESSED' ? `${ res . flashcards . length } flashcards generated from your document` : 'Failed to generate flashcards' ,
137- cardCount : res . flashcards . length ,
138- source : res . documentName || 'Unknown Document' ,
139- difficulty : res . flashcards . length > 10 ? 'Advanced' : 'Beginner' ,
140- status : res . status ,
141- error : res . error ,
142- flashcards : res . flashcards
143- } ;
100+ if ( documentContent . flashcardStatus === 'PROCESSING' ) {
101+ status = 'PROCESSING' ;
102+ errorMessage = 'AI is generating your flashcards...' ;
103+ } else if ( documentContent . flashcardStatus === 'ERROR' ) {
104+ status = 'ERROR' ;
105+ errorMessage = 'Failed to generate flashcards' ;
106+ } else if ( documentContent . flashcardStatus === 'UPLOADED' ) {
107+ status = 'PENDING' ;
108+ errorMessage = 'Waiting for flashcard generation to start...' ;
144109 }
145110
146- // Default: wait for automatic processing
147111 return {
148112 id : documentId ,
149113 title : documentContent . originalName || 'Unknown Document' ,
150- description : 'Waiting for automatic flashcard generation ...',
114+ description : errorMessage || 'Processing ...',
151115 cardCount : 0 ,
152116 source : documentContent . originalName || 'Unknown Document' ,
153117 difficulty : 'Unknown' ,
154- status : 'PROCESSING' ,
155- error : 'Waiting for automatic flashcard generation...' ,
118+ status,
119+ error : errorMessage ,
156120 flashcards : [ ]
157121 } ;
158122
@@ -173,7 +137,6 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
173137
174138 const results = await Promise . all ( flashcardPromises ) ;
175139
176- // Mark as fetched and remove from processing
177140 newDocumentIds . forEach ( id => {
178141 fetchedDocumentIds . current . add ( id ) ;
179142 processingDocumentIds . current . delete ( id ) ;
@@ -192,15 +155,11 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
192155 error : result . error ,
193156 flashcards : result . flashcards
194157 } ) ) ;
195- return [ ...existingDecks , ...mappedResults ] ;
158+ const newDecks = [ ...existingDecks , ...mappedResults ] ;
159+ return newDecks ;
196160 } ) ;
197161 } ;
198162
199- // Start automatic polling for documents with flashcard data
200- for ( const documentId of newDocumentIds ) {
201- pollFlashcardStatus ( documentId ) ;
202- }
203-
204163 fetchFlashcards ( ) ;
205164 } , [ documentIds . join ( "," ) ] ) ; // Keep dependencies minimal to avoid infinite loops
206165
@@ -329,10 +288,14 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
329288 // Filter ready flashcards
330289 const readyFlashcards = flashcardDecks . filter ( deck => deck . status === 'PROCESSED' && deck . flashcards && deck . flashcards . length > 0 ) ;
331290
332- // Display pending documents or errors
333- const pendingDocuments = flashcardDecks . filter ( deck =>
334- ( deck . status !== 'PROCESSED' && deck . status !== 'PROCESSING' && deck . status !== 'ERROR' )
335- ) ;
291+ // Display processing documents
292+ const processingDocuments = flashcardDecks . filter ( deck => deck . status === 'PROCESSING' ) ;
293+
294+ // Display pending documents
295+ const pendingDocuments = flashcardDecks . filter ( deck => deck . status === 'PENDING' ) ;
296+
297+ // Display error documents
298+ const errorDocuments = flashcardDecks . filter ( deck => deck . status === 'ERROR' ) ;
336299
337300 return (
338301 < >
@@ -516,22 +479,48 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
516479 </ div >
517480 ) }
518481
482+ { processingDocuments . length > 0 && (
483+ < div className = "space-y-4" >
484+ < h3 className = "text-lg font-semibold text-gray-900" > Flashcards Being Generated</ h3 >
485+ < div className = "grid gap-6 md:grid-cols-2" >
486+ { processingDocuments . map ( ( deck ) => (
487+ < Card key = { deck . id } className = "border-blue-200 bg-blue-50" >
488+ < CardContent className = "pt-6" >
489+ < div className = "flex items-center justify-between" >
490+ < div className = "flex items-center space-x-3" >
491+ < Loader2 className = "h-5 w-5 text-blue-600 animate-spin" />
492+ < div >
493+ < h4 className = "font-medium text-gray-900" > { deck . title } </ h4 >
494+ < p className = "text-sm text-blue-600" > AI is actively generating your flashcards...</ p >
495+ </ div >
496+ </ div >
497+ < span className = "px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-600" >
498+ PROCESSING
499+ </ span >
500+ </ div >
501+ </ CardContent >
502+ </ Card >
503+ ) ) }
504+ </ div >
505+ </ div >
506+ ) }
507+
519508 { pendingDocuments . length > 0 && (
520509 < div className = "space-y-4" >
521510 < h3 className = "text-lg font-semibold text-gray-900" > Pending Flashcard Decks</ h3 >
522511 < div className = "grid gap-6 md:grid-cols-2" >
523512 { pendingDocuments . map ( ( deck ) => (
524- < Card key = { deck . id } className = "border-gray -200 bg-gray -50" >
513+ < Card key = { deck . id } className = "border-yellow -200 bg-yellow -50" >
525514 < CardContent className = "pt-6" >
526515 < div className = "flex items-center justify-between" >
527516 < div className = "flex items-center space-x-3" >
528- < Loader2 className = "h-5 w-5 text-gray -600 animate-spin" />
517+ < Loader2 className = "h-5 w-5 text-yellow -600 animate-spin" />
529518 < div >
530519 < h4 className = "font-medium text-gray-900" > { deck . title } </ h4 >
531- < p className = "text-sm text-gray -600" > Flashcard generation hasn't started yet </ p >
520+ < p className = "text-sm text-yellow -600" > Waiting for flashcard generation to start... </ p >
532521 </ div >
533522 </ div >
534- < span className = "px-2 py-1 rounded-full text-xs font-medium bg-gray -100 text-gray -600" >
523+ < span className = "px-2 py-1 rounded-full text-xs font-medium bg-yellow -100 text-yellow -600" >
535524 PENDING
536525 </ span >
537526 </ div >
@@ -542,7 +531,33 @@ const FlashcardsTab = ({ uploadedFiles, documentIds }: FlashcardsTabProps) => {
542531 </ div >
543532 ) }
544533
545- { readyFlashcards . length === 0 && pendingDocuments . length === 0 && (
534+ { errorDocuments . length > 0 && (
535+ < div className = "space-y-4" >
536+ < h3 className = "text-lg font-semibold text-gray-900" > Failed Flashcard Decks</ h3 >
537+ < div className = "grid gap-6 md:grid-cols-2" >
538+ { errorDocuments . map ( ( deck ) => (
539+ < Card key = { deck . id } className = "border-red-200 bg-red-50" >
540+ < CardContent className = "pt-6" >
541+ < div className = "flex items-center justify-between" >
542+ < div className = "flex items-center space-x-3" >
543+ < XCircle className = "h-5 w-5 text-red-600" />
544+ < div >
545+ < h4 className = "font-medium text-gray-900" > { deck . title } </ h4 >
546+ < p className = "text-sm text-red-600" > { deck . error || 'Failed to generate flashcards' } </ p >
547+ </ div >
548+ </ div >
549+ < span className = "px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-600" >
550+ ERROR
551+ </ span >
552+ </ div >
553+ </ CardContent >
554+ </ Card >
555+ ) ) }
556+ </ div >
557+ </ div >
558+ ) }
559+
560+ { readyFlashcards . length === 0 && processingDocuments . length === 0 && pendingDocuments . length === 0 && errorDocuments . length === 0 && (
546561 < Card className = "text-center py-12" >
547562 < CardContent >
548563 < BookOpen className = "h-16 w-16 text-gray-400 mx-auto mb-4" />
0 commit comments