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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useState } from 'react'

const DatasetUpload: React.FC = () => {
const [file, setFile] = useState<File | null>(null)
const [isUploading, setIsUploading] = useState(false)
const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The component uses hardcoded strings for status messages on lines 33, 36, and 39. It's a best practice to extract these into a constants object. This improves maintainability, reduces the chance of typos, and makes future internationalization easier.

Example:

const STATUS_MESSAGES = {
  SUCCESS: 'Dataset uploaded successfully!',
  FAILURE: 'Failed to upload dataset. Please try again.',
  NETWORK_ERROR: 'Network error. Please check your connection.',
};

// Then use it like:
setStatus({ type: 'success', message: STATUS_MESSAGES.SUCCESS });


const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0])
setStatus(null)
}
}
Comment on lines +8 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The component currently doesn't perform any client-side validation on the selected file. Based on the backend code, it seems to expect a CSV file. You should add validation to check the file type (e.g., text/csv) and potentially the file size to provide immediate feedback to the user and prevent unnecessary uploads of invalid files. This improves user experience and reduces load on the backend.

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const selectedFile = e.target.files?.[0];
    if (selectedFile) {
      if (!selectedFile.type.includes('csv')) {
        setStatus({ type: 'error', message: 'Invalid file type. Please upload a CSV file.' });
        setFile(null);
        e.target.value = ''; // Reset input to allow re-selecting the same file
        return;
      }

      setFile(selectedFile);
      setStatus(null);
    }
  }


const handleSubmit = async () => {
if (!file) return

if (isUploading) return

setIsUploading(true)
setStatus(null)

const formData = new FormData()
formData.append('file', file)

try {
const response = await fetch('/api/v1/training-datasets/upload', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The API endpoint URL /api/v1/training-datasets/upload is hardcoded. It's better to store it in a central configuration file or as a constant. This makes it easier to manage and update API endpoints, especially if they are used in multiple places or need to change between environments.

For example, you could define a constant at the top of the component:
const UPLOAD_API_ENDPOINT = '/api/v1/training-datasets/upload';
...and then use it in the fetch call.

method: 'POST',
body: formData,
})

if (response.ok) {
setStatus({ type: 'success', message: 'Dataset uploaded successfully!' })
setFile(null)
} else {
setStatus({ type: 'error', message: 'Failed to upload dataset. Please try again.' })
}
Comment on lines +35 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message for a failed upload is generic. The backend might provide a more specific error message in the response body. You should attempt to parse the response to get a more descriptive error message. This will improve the user experience by providing more context about what went wrong.

      } else {
        let errorMessage = 'Failed to upload dataset. Please try again.';
        try {
          const errorData = await response.json();
          if (errorData && errorData.message) {
            errorMessage = errorData.message;
          }
        } catch {
          // Ignore if response is not JSON
        }
        setStatus({ type: 'error', message: errorMessage });
      }

} catch (error) {
setStatus({ type: 'error', message: 'Network error. Please check your connection.' })
} finally {
setIsUploading(false)
}
}

return (
<div className="p-6 bg-gray-900 rounded-lg border border-gray-800 text-white max-w-lg mx-auto mt-10">
<h2 className="text-xl font-semibold mb-4">Upload Training Data</h2>

<div className="flex flex-col gap-4">
<input
type="file"
onChange={handleFileChange}
disabled={isUploading}
className="block w-full text-sm text-gray-400
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-indigo-600 file:text-white
hover:file:bg-indigo-700
disabled:opacity-50 disabled:cursor-not-allowed"
/>
Comment on lines +50 to +61
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file input is missing an associated <label>. Labels are crucial for accessibility as they provide context for screen reader users. You should wrap the input with a label. You can add a visually hidden span inside the label for screen reader text (e.g., using Tailwind's sr-only class).

        <label>
          <span className="sr-only">Choose a file to upload</span>
          <input
            type="file"
            onChange={handleFileChange}
            disabled={isUploading}
            className="block w-full text-sm text-gray-400
              file:mr-4 file:py-2 file:px-4
              file:rounded-full file:border-0
              file:text-sm file:font-semibold
              file:bg-indigo-600 file:text-white
              hover:file:bg-indigo-700
              disabled:opacity-50 disabled:cursor-not-allowed"
          />
        </label>


{status && (
<div
className={`p-3 rounded text-sm ${
status.type === 'success'
? 'bg-green-900/50 text-green-300'
: 'bg-red-900/50 text-red-300'
}`}
>
{status.message}
</div>
Comment on lines +64 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The status message updates dynamically, but screen readers may not announce these changes. To improve accessibility, you should add role="status" and aria-live="polite" to the status message container. This ensures that users of assistive technologies are notified of the upload status.

          <div
            role="status"
            aria-live="polite"
            className={`p-3 rounded text-sm ${
              status.type === 'success'
                ? 'bg-green-900/50 text-green-300'
                : 'bg-red-900/50 text-red-300'
            }`}
          >
            {status.message}
          </div>

)}

<button
onClick={handleSubmit}
disabled={!file || isUploading}
className={`px-4 py-2 rounded font-medium transition-colors
${
!file || isUploading
? 'bg-gray-700 text-gray-400 cursor-not-allowed'
: 'bg-indigo-600 text-white hover:bg-indigo-700'
}
`}
>
{isUploading ? 'Uploading...' : 'Submit Dataset'}
</button>
</div>
</div>
)
}

export default DatasetUpload