Spaces:
Sleeping
Sleeping
| import { useState, useEffect } from 'react'; | |
| import { XIcon } from 'lucide-react'; | |
| // Static defaults for fields requested to be static. Change these constants to update the values shown. | |
| const DEFAULT_PATIENT_ID = '41334'; | |
| const DEFAULT_PHYSICIAN = 'Rajesh Venugopal'; | |
| const DEFAULT_FACILITY = 'Multi-Lab Speciality'; | |
| interface ReportModalProps { | |
| isOpen: boolean; | |
| onClose: () => void; | |
| onSubmit: (formData: FormData) => void; | |
| analysisId: string; | |
| analysisSummaryJson: string; | |
| } | |
| export interface ReportFormData { | |
| patient_id: string; | |
| exam_date: string; | |
| metadata: { | |
| physician: string; | |
| facility: string; | |
| clinical_history?: string; | |
| }; | |
| notes?: string; | |
| analysis_id: string; | |
| } | |
| export function ReportModal({ isOpen, onClose, onSubmit, analysisId, analysisSummaryJson }: ReportModalProps) { | |
| const [formData, setFormData] = useState<ReportFormData>({ | |
| patient_id: DEFAULT_PATIENT_ID, | |
| exam_date: new Date().toISOString().split('T')[0], | |
| metadata: { | |
| physician: DEFAULT_PHYSICIAN, | |
| facility: DEFAULT_FACILITY, | |
| clinical_history: '', | |
| }, | |
| notes: '', | |
| analysis_id: analysisId, | |
| }); | |
| // Keep internal form state in sync when the parent passes a new analysisId | |
| useEffect(() => { | |
| setFormData(prev => ({ ...prev, analysis_id: analysisId })); | |
| }, [analysisId]); | |
| if (!isOpen) return null; | |
| const handleSubmit = (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| // Build FormData for FastAPI endpoint | |
| const payload = new FormData(); | |
| payload.append("patient_id", formData.patient_id); | |
| payload.append("exam_date", formData.exam_date); | |
| payload.append("metadata", JSON.stringify(formData.metadata)); | |
| payload.append("notes", formData.notes || ""); | |
| payload.append("analysis_id", formData.analysis_id); | |
| payload.append("analysis_summary", analysisSummaryJson); | |
| // Pass the FormData object to the parent onSubmit | |
| onSubmit(payload); | |
| }; | |
| const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |
| const { name, value } = e.target; | |
| if (name.startsWith('metadata.')) { | |
| const field = name.split('.')[1]; | |
| setFormData(prev => ({ | |
| ...prev, | |
| metadata: { | |
| ...prev.metadata, | |
| [field]: value, | |
| }, | |
| })); | |
| } else { | |
| setFormData(prev => ({ | |
| ...prev, | |
| [name]: value, | |
| })); | |
| } | |
| }; | |
| return ( | |
| <div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"> | |
| <div className="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto"> | |
| <div className="p-6 space-y-6"> | |
| <div className="flex items-center justify-between border-b pb-3"> | |
| <h2 className="text-2xl font-semibold text-gray-900">Generate Medical Report</h2> | |
| <button | |
| onClick={onClose} | |
| className="text-gray-400 hover:text-gray-500" | |
| > | |
| <XIcon className="w-6 h-6" /> | |
| </button> | |
| </div> | |
| <form onSubmit={handleSubmit} className="space-y-6"> | |
| {/* Patient Information */} | |
| <div className="space-y-4"> | |
| <h3 className="text-lg font-medium text-gray-900">Patient Information</h3> | |
| <div className="grid grid-cols-1 gap-4 sm:grid-cols-2"> | |
| <div> | |
| <label htmlFor="patient_id" className="block text-sm font-medium text-gray-700"> | |
| Patient ID * | |
| </label> | |
| <input | |
| required | |
| type="text" | |
| name="patient_id" | |
| id="patient_id" | |
| value={formData.patient_id} | |
| onChange={handleChange} | |
| readOnly | |
| className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" | |
| /> | |
| </div> | |
| <div> | |
| <label htmlFor="exam_date" className="block text-sm font-medium text-gray-700"> | |
| Exam Date * | |
| </label> | |
| <input | |
| required | |
| type="date" | |
| name="exam_date" | |
| id="exam_date" | |
| value={formData.exam_date} | |
| onChange={handleChange} | |
| className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Slide Metadata */} | |
| <div className="space-y-4"> | |
| <h3 className="text-lg font-medium text-gray-900">Slide Metadata</h3> | |
| <div className="grid grid-cols-1 gap-4 sm:grid-cols-2"> | |
| <div> | |
| <label htmlFor="metadata.physician" className="block text-sm font-medium text-gray-700"> | |
| Physician Name * | |
| </label> | |
| <input | |
| required | |
| type="text" | |
| name="metadata.physician" | |
| id="metadata.physician" | |
| value={formData.metadata.physician} | |
| onChange={handleChange} | |
| readOnly | |
| className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" | |
| /> | |
| </div> | |
| <div> | |
| <label htmlFor="metadata.facility" className="block text-sm font-medium text-gray-700"> | |
| Facility * | |
| </label> | |
| <input | |
| required | |
| type="text" | |
| name="metadata.facility" | |
| id="metadata.facility" | |
| value={formData.metadata.facility} | |
| onChange={handleChange} | |
| readOnly | |
| className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" | |
| /> | |
| </div> | |
| <div> | |
| <label htmlFor="metadata.clinical_history" className="block text-sm font-medium text-gray-700"> | |
| Clinical History | |
| </label> | |
| <input | |
| type="text" | |
| name="metadata.clinical_history" | |
| id="metadata.clinical_history" | |
| value={formData.metadata.clinical_history} | |
| onChange={handleChange} | |
| className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Notes */} | |
| <div className="space-y-4"> | |
| <h3 className="text-lg font-medium text-gray-900">Doctor's Notes</h3> | |
| <textarea | |
| name="notes" | |
| id="notes" | |
| rows={4} | |
| value={formData.notes} | |
| onChange={handleChange} | |
| placeholder="Add any additional notes or observations..." | |
| className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" | |
| /> | |
| </div> | |
| {/* Actions */} | |
| <div className="flex items-center justify-end space-x-3 pt-4 border-t"> | |
| <button | |
| type="button" | |
| onClick={onClose} | |
| className="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-500" | |
| > | |
| Cancel | |
| </button> | |
| <button | |
| type="submit" | |
| className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | |
| > | |
| Generate Report | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |