Spaces:
Sleeping
Sleeping
| import React, { useRef } from 'react'; | |
| import { UploadIcon } from 'lucide-react'; | |
| interface UploadSectionProps { | |
| selectedTest: string; | |
| uploadedImage: string | null; | |
| setUploadedImage: (image: string | null) => void; | |
| selectedModel: string; | |
| setSelectedModel: (model: string) => void; | |
| onAnalyze: () => void; | |
| } | |
| export function UploadSection({ | |
| selectedTest, | |
| uploadedImage, | |
| setUploadedImage, | |
| selectedModel, | |
| setSelectedModel, | |
| onAnalyze, | |
| }: UploadSectionProps) { | |
| const fileInputRef = useRef<HTMLInputElement>(null); | |
| const modelOptions = { | |
| cytology: [ | |
| { value: 'mwt', label: 'Manalife_AI_MWT' }, | |
| { value: 'yolo', label: 'Manalife_AI_YOLOv8' }, | |
| ], | |
| colposcopy: [ | |
| { value: 'cin', label: 'Manalife_MaANIA_Colpo' }, | |
| ], | |
| histopathology: [ | |
| { value: 'histopathology', label: 'ManalifeAI__Path Foundation Model' }, | |
| ], | |
| }; | |
| const sampleImages = { | |
| cytology: [ | |
| "/cyto/neg1.bmp", | |
| "/cyto/neg2.png", | |
| "/cyto/LSIL1.png", | |
| "/cyto/LSIL2.png", | |
| "/cyto/HSIL1.jpg", | |
| "/cyto/HSIL2.JPG", | |
| ], | |
| colposcopy: [ | |
| "/colpo/ab1.jpg", | |
| "/colpo/ab2.jpg", | |
| "/colpo/ab3.jpg", | |
| "/colpo/nor1.jpg", | |
| "/colpo/nor2.jpg", | |
| "/colpo/nor3.jpg", | |
| ], | |
| histopathology: [ | |
| "/histo/hist1.png", | |
| "/histo/hist2.png", | |
| "/histo/hist3.jpg", | |
| ], | |
| }; | |
| const currentModels = | |
| modelOptions[selectedTest as keyof typeof modelOptions] || []; | |
| const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const file = e.target.files?.[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| setUploadedImage(event.target?.result as string); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| }; | |
| const handleDrop = (e: React.DragEvent) => { | |
| e.preventDefault(); | |
| const file = e.dataTransfer.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| setUploadedImage(event.target?.result as string); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| }; | |
| // Handle click on sample image | |
| const handleSampleClick = (imgUrl: string) => { | |
| setUploadedImage(imgUrl); | |
| }; | |
| return ( | |
| <div className="bg-white rounded-lg shadow-sm p-6"> | |
| <h2 className="text-2xl font-semibold text-gray-900 mb-6"> | |
| Upload an image of a tissue sample | |
| </h2> | |
| {/* Upload Area */} | |
| <div | |
| onDrop={handleDrop} | |
| onDragOver={(e) => e.preventDefault()} | |
| onClick={() => fileInputRef.current?.click()} | |
| className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer hover:border-blue-400 transition-colors" | |
| > | |
| <input | |
| ref={fileInputRef} | |
| type="file" | |
| accept="image/*" | |
| onChange={handleFileChange} | |
| className="hidden" | |
| /> | |
| <div className="flex flex-col items-center"> | |
| <div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4"> | |
| <UploadIcon className="w-8 h-8 text-gray-400" /> | |
| </div> | |
| {uploadedImage ? ( | |
| <> | |
| <p className="text-teal-700 font-medium mb-2"> | |
| Image uploaded successfully! | |
| </p> | |
| <p className="text-sm text-gray-500 mb-4"> | |
| Click to upload a different image | |
| </p> | |
| <div className="w-32 h-32 rounded-lg overflow-hidden border border-gray-200"> | |
| <img | |
| src={uploadedImage} | |
| alt="Uploaded sample" | |
| className="w-full h-full object-cover" | |
| /> | |
| </div> | |
| </> | |
| ) : ( | |
| <p className="text-gray-600">Drag and drop or click to upload</p> | |
| )} | |
| </div> | |
| </div> | |
| {/* Model Selection */} | |
| <div className="mt-6"> | |
| <label className="block text-sm font-medium text-gray-700 mb-2"> | |
| Select Analysis Model: | |
| </label> | |
| <select | |
| value={selectedModel} | |
| onChange={(e) => setSelectedModel(e.target.value)} | |
| className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" | |
| > | |
| <option value="">Choose a model...</option> | |
| {currentModels.map((model) => ( | |
| <option key={model.value} value={model.value}> | |
| {model.label} | |
| </option> | |
| ))} | |
| </select> | |
| </div> | |
| {/* Analyze Button */} | |
| <button | |
| type="button" | |
| onClick={(e) => { | |
| // Prevent clicks from bubbling to the upload area (which would trigger file select) | |
| e.stopPropagation(); | |
| onAnalyze(); | |
| }} | |
| disabled={!uploadedImage || !selectedModel} | |
| className={`w-full mt-6 text-white py-3 rounded-lg font-medium transition-colors ${ | |
| !uploadedImage || !selectedModel | |
| ? 'bg-gray-300 disabled:cursor-not-allowed' | |
| : 'bg-gradient-to-r from-teal-800 to-teal-600 hover:opacity-95' | |
| }`} | |
| > | |
| Analyze | |
| </button> | |
| {/* Separator */} | |
| <hr className="my-8 border-gray-200" /> | |
| {/* Sample Images Section */} | |
| <div> | |
| <h3 className="text-lg font-semibold text-gray-800 mb-4"> | |
| Samples Images | |
| </h3> | |
| <div className="flex flex-wrap gap-4"> | |
| {(sampleImages[selectedTest as keyof typeof sampleImages] || []).map( | |
| (img, index) => ( | |
| <div | |
| key={index} | |
| className={`w-20 h-20 rounded-lg border-2 cursor-pointer transition-transform hover:scale-105 overflow-hidden ${ | |
| uploadedImage === img ? 'ring-2 ring-offset-1 ring-blue-800' : 'border-gray-300' | |
| }`} | |
| onClick={() => handleSampleClick(img)} | |
| > | |
| <img src={img} alt={`Sample ${index + 1}`} className="w-full h-full object-cover" /> | |
| </div> | |
| ) | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |