@@ -31,7 +31,7 @@ import { Light as SyntaxHighlighter } from "react-syntax-highlighter"
3131import { vs } from "react-syntax-highlighter/dist/esm/styles/hljs"
3232import sql from "react-syntax-highlighter/dist/cjs/languages/hljs/sql"
3333import { useNavigate , useParams } from "react-router-dom"
34- import { useState , useEffect , useCallback } from "react"
34+ import { useState , useEffect , useCallback , useRef } from "react"
3535import { getApiUrl , headerBuilder } from '../api/config' ;
3636import BatchHistoryPanel from "../components/batchHistoryPanel"
3737import PanelRight from "../components/Panels/PanelRight" ;
@@ -501,6 +501,7 @@ const ModernizationPage = () => {
501501 const [ isZipButtonDisabled , setIsZipButtonDisabled ] = useState ( true ) ;
502502 const [ fileLoading , setFileLoading ] = useState ( false ) ;
503503 const [ lastActivityTime , setLastActivityTime ] = useState < number > ( Date . now ( ) ) ;
504+ const hasNavigatedRef = useRef ( false ) ;
504505 //const [pageLoadTime] = useState<number>(Date.now());
505506
506507 // Fetch file content when a file is selected
@@ -536,12 +537,22 @@ const ModernizationPage = () => {
536537 setBatchSummary ( data ) ;
537538 if ( data ) {
538539
539- const batchCompleted = data . status ?. toLowerCase ( ) === "completed" || data . status === "failed" ;
540- if ( batchCompleted ) {
540+ const batchCompleted = data . status ?. toLowerCase ( ) === "completed" || data . status ?. toLowerCase ( ) === "failed" ;
541+ const allFilesTerminal = data . files . every ( ( file : any ) =>
542+ [ "completed" , "failed" , "error" ] . includes ( file . status ?. toLowerCase ( ) || "" )
543+ ) ;
544+
545+ if ( batchCompleted || allFilesTerminal ) {
541546 setAllFilesCompleted ( true ) ;
542547 if ( data . hasFiles > 0 ) {
543548 setIsZipButtonDisabled ( false ) ;
544549 }
550+ // Batch already finished (e.g., all files were harmful content) — navigate directly
551+ if ( ! hasNavigatedRef . current ) {
552+ hasNavigatedRef . current = true ;
553+ navigate ( `/batch-view/${ batchId } ` ) ;
554+ return ;
555+ }
545556 }
546557 // Transform the server response to an array of your FileItem objects
547558 const fileItems : FileItem [ ] = data . files . map ( ( file : any , index : number ) => ( {
@@ -725,7 +736,10 @@ const ModernizationPage = () => {
725736 // Update files state when Redux fileList changes
726737 useEffect ( ( ) => {
727738 if ( reduxFileList && reduxFileList . length > 0 ) {
728- setAllFilesCompleted ( false ) ;
739+ // Only reset completion state if not already finalized
740+ if ( ! allFilesCompleted ) {
741+ setAllFilesCompleted ( false ) ;
742+ }
729743 // Map the Redux fileList to our FileItem format
730744 const fileItems : FileItem [ ] = reduxFileList . filter ( file => file . type !== 'summary' ) . map ( ( file : any , index : number ) => ( {
731745
@@ -832,8 +846,11 @@ const ModernizationPage = () => {
832846 } ) ;
833847
834848 // Navigate only after all files have reached terminal states.
835- console . log ( "Processing complete (all files done), navigating to batch view page" ) ;
836- navigate ( `/batch-view/${ batchId } ` ) ;
849+ if ( ! hasNavigatedRef . current ) {
850+ hasNavigatedRef . current = true ;
851+ console . log ( "Processing complete (all files done), navigating to batch view page" ) ;
852+ navigate ( `/batch-view/${ batchId } ` ) ;
853+ }
837854 }
838855 } catch ( err ) {
839856 console . error ( "Failed to update summary status:" , err ) ;
@@ -996,6 +1013,43 @@ useEffect(() => {
9961013 return ( ) => clearInterval ( checkInactivity ) ;
9971014 } , [ lastActivityTime , files , allFilesCompleted , updateSummaryStatus , navigate , batchId ] ) ;
9981015
1016+ // Fallback polling: periodically re-fetch batch summary to catch cases where
1017+ // WebSocket events were missed (e.g., all files were harmful and processed
1018+ // before WebSocket connected). Polls every 5 seconds until all files are done.
1019+ useEffect ( ( ) => {
1020+ if ( allFilesCompleted || ! batchId || hasNavigatedRef . current ) return ;
1021+
1022+ const pollInterval = setInterval ( async ( ) => {
1023+ if ( hasNavigatedRef . current || allFilesCompleted ) {
1024+ clearInterval ( pollInterval ) ;
1025+ return ;
1026+ }
1027+ try {
1028+ const data = await fetchBatchSummary ( batchId ) ;
1029+ if ( ! data ) return ;
1030+
1031+ const batchTerminal = [ "completed" , "failed" ] . includes ( data . status ?. toLowerCase ( ) || "" ) ;
1032+ const allTerminal = data . files . every ( ( file : any ) =>
1033+ [ "completed" , "failed" , "error" ] . includes ( file . status ?. toLowerCase ( ) || "" )
1034+ ) ;
1035+
1036+ if ( batchTerminal || allTerminal ) {
1037+ console . log ( "Fallback poll detected batch completion, navigating to batch view" ) ;
1038+ clearInterval ( pollInterval ) ;
1039+ setAllFilesCompleted ( true ) ;
1040+ if ( ! hasNavigatedRef . current ) {
1041+ hasNavigatedRef . current = true ;
1042+ navigate ( `/batch-view/${ batchId } ` ) ;
1043+ }
1044+ }
1045+ } catch ( err ) {
1046+ console . error ( "Fallback poll error:" , err ) ;
1047+ }
1048+ } , 5000 ) ;
1049+
1050+ return ( ) => clearInterval ( pollInterval ) ;
1051+ } , [ batchId , allFilesCompleted , navigate ] ) ;
1052+
9991053
10001054 useEffect ( ( ) => {
10011055 console . log ( 'Current files state:' , files ) ;
0 commit comments