@ -1,6 +1,10 @@
import { useCallback , useState } from 'react' ;
import React from 'react' ;
import { useCallback , useEffect , useState } from 'react' ;
import { useDropzone } from 'react-dropzone' ;
import { useDispatch } from 'react-redux' ;
import { ActiveState } from '../models/misc' ;
import { getDocs } from '../preferences/preferenceApi' ;
import { setSourceDocs } from '../preferences/preferenceSlice' ;
export default function Upload ( {
modalState ,
@ -11,6 +15,85 @@ export default function Upload({
} ) {
const [ docName , setDocName ] = useState ( '' ) ;
const [ files , setfiles ] = useState < File [ ] > ( [ ] ) ;
const [ progress , setProgress ] = useState < {
type : 'UPLOAD' | 'TRAINIING' ;
percentage : number ;
taskId? : string ;
} > ( ) ;
function Progress ( {
title ,
isCancellable = false ,
} : {
title : string ;
isCancellable? : boolean ;
} ) {
return (
< div className = "mt-5 flex flex-col items-center gap-2" >
< p className = "text-xl tracking-[0.15px]" > { title } . . . < / p >
< p className = "text-sm text-gray-2000" > This may take several minutes < / p >
< p className = "mt-10 text-2xl" > { progress ? . percentage || 0 } % < / p >
< div className = "mb-10 w-[50%]" >
< div className = "h-1 w-[100%] bg-blue-4000" > < / div >
< div
className = { ` relative bottom-1 h-1 bg-blue-5000 transition-all ` }
style = { { width : ` ${ progress ? . percentage || 0 } % ` } }
> < / div >
< / div >
< button
onClick = { ( ) = > {
setDocName ( '' ) ;
setfiles ( [ ] ) ;
setProgress ( undefined ) ;
setModalState ( 'INACTIVE' ) ;
} }
className = { ` rounded-md bg-blue-3000 px-4 py-2 text-sm font-medium text-white ${
isCancellable ? '' : 'hidden'
} ` }
>
Finish
< / button >
< / div >
) ;
}
function UploadProgress() {
return < Progress title = "Upload is in progress" > < / Progress > ;
}
function TrainingProgress() {
const dispatch = useDispatch ( ) ;
useEffect ( ( ) = > {
( progress ? . percentage ? ? 0 ) < 100 &&
setTimeout ( ( ) = > {
const apiHost = import . meta . env . VITE_API_HOST ;
fetch ( ` ${ apiHost } /api/task_status?task_id= ${ progress ? . taskId } ` )
. then ( ( data ) = > data . json ( ) )
. then ( ( data ) = > {
if ( data . status == 'SUCCESS' ) {
getDocs ( ) . then ( ( data ) = > dispatch ( setSourceDocs ( data ) ) ) ;
setProgress (
( progress ) = > progress && { . . . progress , percentage : 100 } ,
) ;
} else {
setProgress (
( progress ) = >
progress && {
. . . progress ,
percentage : data.result.current ,
} ,
) ;
}
} ) ;
} , 5000 ) ;
} , [ progress , dispatch ] ) ;
return (
< Progress
title = "Training is in progress"
isCancellable = { progress ? . percentage === 100 }
> < / Progress >
) ;
}
const onDrop = useCallback ( ( acceptedFiles : File [ ] ) = > {
setfiles ( acceptedFiles ) ;
@ -19,36 +102,25 @@ export default function Upload({
const doNothing = ( ) = > undefined ;
const uploadFile = async ( ) = > {
const uploadFile = ( ) = > {
const formData = new FormData ( ) ;
// Add the uploaded files to formData
files . forEach ( ( file ) = > {
formData . append ( 'file' , file ) ;
} ) ;
// Add the document name to formData
formData . append ( 'name' , docName ) ;
formData . append ( 'user' , 'local' ) ;
const apiHost = import . meta . env . VITE_API_HOST ;
try {
const response = await fetch ( apiHost + '/api/upload' , {
method : 'POST' ,
body : formData ,
const xhr = new XMLHttpRequest ( ) ;
xhr . upload . addEventListener ( 'progress' , ( event ) = > {
const progress = + ( ( event . loaded / event . total ) * 100 ) . toFixed ( 2 ) ;
setProgress ( { type : 'UPLOAD' , percentage : progress } ) ;
} ) ;
if ( response . ok ) {
console . log ( 'Files uploaded successfully' ) ;
setDocName ( '' ) ;
setfiles ( [ ] ) ;
setModalState ( 'INACTIVE' ) ;
} else {
console . error ( 'File upload failed' ) ;
}
} catch ( error ) {
console . error ( 'Error during file upload:' , error ) ;
}
xhr . onload = ( ) = > {
const { task_id } = JSON . parse ( xhr . responseText ) ;
setProgress ( { type : 'TRAINIING' , percentage : 0 , taskId : task_id } ) ;
} ;
xhr . open ( 'POST' , ` ${ apiHost + '/api/upload' } ` ) ;
xhr . send ( formData ) ;
} ;
const { getRootProps , getInputProps , isDragActive } = useDropzone ( {
@ -58,13 +130,15 @@ export default function Upload({
onDragOver : doNothing ,
onDragLeave : doNothing ,
} ) ;
return (
< article
className = { ` ${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} absolute z - 30 h - screen w - screen bg - gray - alpha ` }
>
< article className = "mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg" >
let view ;
if ( progress ? . type === 'UPLOAD' ) {
view = < UploadProgress > < / UploadProgress > ;
} else if ( progress ? . type === 'TRAINIING' ) {
view = < TrainingProgress > < / TrainingProgress > ;
} else {
view = (
< >
< p className = "mb-7 text-xl text-jet" > Upload New Documentation < / p >
< input
type = "text"
@ -76,10 +150,10 @@ export default function Upload({
< span className = "bg-white px-2 text-xs text-gray-4000" > Name < / span >
< / div >
< div { ...getRootProps ( ) } >
< label className = "rounded-md border border-blue-2000 px-4 py-2 font-medium text-blue-2000 hover:cursor-pointer" >
< span className = "rounded-md border border-blue-2000 px-4 py-2 font-medium text-blue-2000 hover:cursor-pointer" >
< input type = "button" { ...getInputProps ( ) } / >
Choose Files
< / label >
< / span >
< / div >
< div className = "mt-9" >
< p className = "mb-5 font-medium text-eerie-black" > Uploaded Files < / p >
@ -108,9 +182,20 @@ export default function Upload({
Cancel
< / button >
< / div >
< / >
) ;
}
return (
< article
className = { ` ${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} absolute z - 30 h - screen w - screen bg - gray - alpha ` }
>
< article className = "mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg" >
{ view }
< / article >
< / article >
) ;
}
// TODO: sanitize all inputs