Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions frontend/src/components/progress-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { CSSProperties } from 'react';

export default function ProgressBar({ progress }: { progress: number }) {
const containerStyles: CSSProperties = {
height: 20,
width: '100%',
backgroundColor: '#e0e0de',
borderRadius: 50,
marginTop: 10,
};

const fillerStyles: CSSProperties = {
height: '100%',
width: `${progress}%`,
backgroundColor: 'var(--primary)',
borderRadius: 'inherit',
textAlign: 'right',
transition: 'width 0.2s ease-in-out',
};

const labelStyles: CSSProperties = {
padding: 5,
color: 'white',
fontWeight: 'bold',
};

return (
<div style={containerStyles} className='mr-4'>
<div style={fillerStyles}>
<span style={labelStyles}>{`${progress}%`}</span>
</div>
</div>
);
}
47 changes: 38 additions & 9 deletions frontend/src/components/upload-component.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use client'

import {useState} from 'react'
import {Cloud} from 'react-feather'
import {useDropzone} from 'react-dropzone'
import {toast} from 'sonner'
import { useState } from 'react'
import { useDropzone } from 'react-dropzone'
import { Cloud } from 'react-feather'
import { toast } from 'sonner'

import {Button} from '@/components/ui/button'
import {uploadDocuments} from '@/lib/documents'
import { Button } from '@/components/ui/button'
import { uploadDocuments } from '@/lib/documents'
import ProgressBar from './progress-bar'

const ACCEPTED_FILE_TYPES = {
'application/pdf': ['.pdf'],
Expand All @@ -22,12 +23,35 @@ export default function UploadComponent({
callback?: () => void
}) {
const [isUploading, setIsUploading] = useState(false)
const [uploadProgress, setUploadProgress] = useState(0);

const {getRootProps, getInputProps, isDragActive} = useDropzone({
accept: ACCEPTED_FILE_TYPES,
maxSize: MAX_FILE_SIZE,
onDrop: async (documents) => {
setIsUploading(true)
await uploadDocuments(courseId, documents)
await uploadDocuments(courseId, documents, (progressEvent) => {
if (progressEvent.total) {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total,
);
setUploadProgress(percentCompleted);
}
})
.then((result) => {
if (result.ok) {
toast.success('Files uploaded successfully')
} else {
toast.error(result.error?.message || 'Failed to upload files')
}
})
.catch(() => {
toast.error('Failed to upload files')
})
.finally(async () => {
setUploadProgress(0); // Reset progress after upload completes
setIsUploading(false)
})
if (callback) {
await callback()
}
Expand Down Expand Up @@ -69,7 +93,12 @@ export default function UploadComponent({
>
<input {...getInputProps()} />
<Cloud className='mx-auto h-12 w-12 text-muted-foreground mb-4' />
<div className='space-y-2'>
{isUploading ? (
<div className='space-y-2'>
<p>Uploading...</p>
<ProgressBar progress={uploadProgress} />
</div>
) : (<div className='space-y-2'>
<p className='text-lg font-medium'>
{isDragActive ? 'Drop files here' : 'Drag and drop files here'}
</p>
Expand All @@ -87,7 +116,7 @@ export default function UploadComponent({
>
{isUploading ? 'Uploading...' : 'Browse Files'}
</Button>
</div>
</div>)}
</div>
)
}
9 changes: 6 additions & 3 deletions frontend/src/lib/documents.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {DocumentsService} from '@/client'
import { DocumentsService } from '@/client'

import {Result} from './result'
import {mapApiError} from './mapApiError'
import { AxiosProgressEvent } from 'axios'
import { mapApiError } from './mapApiError'
import { Result } from './result'

export async function uploadDocuments(
courseId: string,
documents: File[],
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void,
): Promise<Result<any>> {
try {
const formData = new FormData()
Expand All @@ -22,6 +24,7 @@ export async function uploadDocuments(
// openapi-ts misinterpret this as a plain string rather than a File
// object. This bypasses the validation
requestValidator: async () => {},
onUploadProgress: onUploadProgress,
})

return {
Expand Down