diff --git a/frontend/components/uploadsystem/segments/uploadfiresql.tsx b/frontend/components/uploadsystem/segments/uploadfiresql.tsx index b839a4f5..381e91e5 100644 --- a/frontend/components/uploadsystem/segments/uploadfiresql.tsx +++ b/frontend/components/uploadsystem/segments/uploadfiresql.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { ReviewStates, UploadFireProps } from '@/config/macros/uploadsystemmacros'; import { FileCollectionRowSet, FileRow, FileRowSet, FormType, getTableHeaders, RequiredTableHeadersByFormType } from '@/config/macros/formdetails'; -import { Box, LinearProgress, Stack, Typography } from '@mui/joy'; +import { Box, LinearProgress, Stack, Typography, useTheme } from '@mui/joy'; import { useOrgCensusContext, usePlotContext, useQuadratContext } from '@/app/contexts/userselectionprovider'; import { useSession } from 'next-auth/react'; import Papa, { parse, ParseResult } from 'papaparse'; @@ -10,6 +10,7 @@ import moment from 'moment'; import PQueue from 'p-queue'; import Divider from '@mui/joy/Divider'; import 'moment-duration-format'; +import { DotLottieReact } from '@lottiefiles/dotlottie-react'; const UploadFireSQL: React.FC = ({ personnelRecording, @@ -34,6 +35,8 @@ const UploadFireSQL: React.FC = ({ const [completedChunks, setCompletedChunks] = useState(0); const [failedChunks, setFailedChunks] = useState>(new Set()); const [etc, setETC] = useState(''); + const [totalProcessingTime, setTotalProcessingTime] = useState(0); + const [chunkStartTime, setChunkStartTime] = useState(0); const [userID, setUserID] = useState(null); const chunkSize = 4096; const connectionLimit = 10; @@ -71,6 +74,32 @@ const UploadFireSQL: React.FC = ({ }); }; + useEffect(() => { + if (completedChunks < 3) { + setETC('Calculating...'); + return; + } + + // Track cumulative processing time + setTotalProcessingTime(prev => prev + (performance.now() - chunkStartTime)); + + // Calculate exponential moving average for smoother chunk time estimation + const smoothingFactor = 0.2; // Adjust between 0 and 1 for desired smoothing (higher = more reactive) + const lastChunkTime = performance.now() - chunkStartTime; + const smoothedAvgTimePerChunk = smoothingFactor * lastChunkTime + (1 - smoothingFactor) * (totalProcessingTime / completedChunks); + + // Calculate remaining chunks and ETC + const remainingChunks = totalChunks - completedChunks; + const estimatedTimeLeft = smoothedAvgTimePerChunk * remainingChunks; + + setETC( + moment.utc(estimatedTimeLeft).format('mm:ss').split(':')[0] + + ' minutes and ' + + moment.utc(estimatedTimeLeft).format('mm:ss').split(':')[1] + + ' seconds remaining...' + ); + }, [completedChunks, totalChunks]); + const parseFileInChunks = async (file: File, delimiter: string) => { let activeTasks = 0; queue.clear(); @@ -141,17 +170,6 @@ const UploadFireSQL: React.FC = ({ return value; }; - const startTime = performance.now(); - let totalProcessingTime = 0; - - const updateETC = () => { - if (completedChunks === 0) return 'Calculating...'; - const avgTimePerChunk = totalProcessingTime / completedChunks; // Average time per chunk - const remainingChunks = totalChunks - completedChunks; - const estimatedTimeLeft = avgTimePerChunk * remainingChunks; - return moment.duration(estimatedTimeLeft, 'milliseconds').format('HH:mm:ss'); - }; - await new Promise((resolve, reject) => { Papa.parse(file, { delimiter: delimiter, @@ -161,7 +179,7 @@ const UploadFireSQL: React.FC = ({ transformHeader, transform, async chunk(results: ParseResult, parser) { - const chunkStartTime = performance.now(); + setChunkStartTime(performance.now()); try { if (activeTasks >= connectionLimit) { parser.pause(); @@ -240,10 +258,6 @@ const UploadFireSQL: React.FC = ({ parser.resume(); queue.start(); } - totalProcessingTime += performance.now() - chunkStartTime; - const etc = updateETC(); - console.log(`Estimated Time to Completion: ${etc}`); - setETC(etc); // Assuming `setETC` updates a state or display } }); } catch (err) { @@ -440,6 +454,8 @@ const UploadFireSQL: React.FC = ({ } }, [hasUploaded.current, errorRows]); + const { palette } = useTheme(); + return ( <> {!hasUploaded.current ? ( @@ -483,35 +499,39 @@ const UploadFireSQL: React.FC = ({ }} > - LOADING: {`${((completedChunks / totalChunks) * 100).toFixed(2)}%`} + LOADING: {`${((completedChunks / totalChunks) * 100).toFixed(2)}%`} {' | '} {completedChunks} completed out of {totalChunks} {' | '} + {totalChunks - completedChunks} remaining
- {completedChunks} completed out of {totalChunks}: {totalChunks - completedChunks} remaining + Estimated time to completion: {etc}
- {failedChunks.size > 0 && - Array.from(failedChunks).map(chunk => ( - - - Completed retry for chunk {chunk + 1}/{totalChunks} - - - - Failed Chunk: {chunk} - - - - ))} + + + + Please do not exit this page! The upload will take some time to complete. + + + )} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d6d90d91..ce72b608 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ "@emotion/cache": "^11.13.1", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@lottiefiles/dotlottie-react": "^0.12.3", "@mui/icons-material": "^6.1.4", "@mui/joy": "^5.0.0-beta.51", "@mui/lab": "^6.0.0-beta.12", @@ -1407,6 +1408,24 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@lottiefiles/dotlottie-react": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.12.3.tgz", + "integrity": "sha512-b0k0Lakj2hmhyIkwJSpKySh6xoelpgWRaijyrCb6fraOCnzuitKglVTtYs0VWf72rk+2aSmC/0IK8j8wruVfOw==", + "license": "MIT", + "dependencies": { + "@lottiefiles/dotlottie-web": "0.40.1" + }, + "peerDependencies": { + "react": "^17 || ^18 || ^19" + } + }, + "node_modules/@lottiefiles/dotlottie-web": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.40.1.tgz", + "integrity": "sha512-iNz1rr8zRTSJ0xCZEPeoCX6YnaNhBdkF7gJWhSa8bwB4NTrYZm0vAuC0jF/aLLhU7q9nf6ykxffrz0Sf4JPSPg==", + "license": "MIT" + }, "node_modules/@monaco-editor/loader": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0004f39b..28a17f86 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "@emotion/cache": "^11.13.1", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@lottiefiles/dotlottie-react": "^0.12.3", "@mui/icons-material": "^6.1.4", "@mui/joy": "^5.0.0-beta.51", "@mui/lab": "^6.0.0-beta.12",