Spaces:
Build error
Build error
| import { useState } from 'react'; | |
| import { useDropzone } from 'react-dropzone'; | |
| import { Upload, FileText, AlertCircle } from 'lucide-react'; | |
| import { Card, CardContent, CardHeader, CardTitle } from './ui/Card'; | |
| import { Button } from './ui/Button'; | |
| export default function NotebookParser() { | |
| const [file, setFile] = useState(null); | |
| const [parsing, setParsing] = useState(false); | |
| const [results, setResults] = useState([]); | |
| const [error, setError] = useState(null); | |
| const onDrop = (acceptedFiles) => { | |
| const notebookFile = acceptedFiles.find(f => f.name.endsWith('.ipynb')); | |
| if (notebookFile) { | |
| setFile(notebookFile); | |
| setError(null); | |
| } else { | |
| setError('Please upload a valid .ipynb file'); | |
| } | |
| }; | |
| const { getRootProps, getInputProps, isDragActive } = useDropzone({ | |
| onDrop, | |
| accept: { 'application/json': ['.ipynb'] }, | |
| maxFiles: 1, | |
| }); | |
| const parseNotebook = async () => { | |
| if (!file) return; | |
| setParsing(true); | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| try { | |
| const response = await fetch('/api/parse-notebook', { | |
| method: 'POST', | |
| body: formData, | |
| }); | |
| if (!response.ok) throw new Error('Parse failed'); | |
| const data = await response.json(); | |
| setResults(data.parameters || []); | |
| } catch (err) { | |
| setError(err.message); | |
| } finally { | |
| setParsing(false); | |
| } | |
| }; | |
| return ( | |
| <div className="space-y-6"> | |
| <h2 className="text-3xl font-bold text-gray-800">Notebook Parser</h2> | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Upload Jupyter Notebook</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div | |
| {...getRootProps()} | |
| className={`border-2 border-dashed rounded-lg p-8 text-center cursor-pointer transition-colors ${ | |
| isDragActive ? 'border-primary-500 bg-primary-50' : 'border-gray-300' | |
| }`} | |
| > | |
| <input {...getInputProps()} /> | |
| <Upload className="w-12 h-12 mx-auto mb-4 text-gray-400" /> | |
| <p className="text-lg">Drag & drop your .ipynb file here</p> | |
| <p className="text-sm text-gray-500 mt-2">or click to browse</p> | |
| </div> | |
| {file && ( | |
| <div className="mt-4 flex items-center"> | |
| <FileText className="w-5 h-5 mr-2 text-primary-500" /> | |
| <span>{file.name}</span> | |
| </div> | |
| )} | |
| {error && ( | |
| <div className="mt-4 flex items-center text-red-500"> | |
| <AlertCircle className="w-5 h-5 mr-2" /> | |
| <span>{error}</span> | |
| </div> | |
| )} | |
| <Button | |
| onClick={parseNotebook} | |
| disabled={!file || parsing} | |
| className="mt-6 w-full" | |
| > | |
| {parsing ? 'Parsing...' : 'Parse Notebook'} | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| {results.length > 0 && ( | |
| <Card> | |
| <CardHeader> | |
| <CardTitle>Extracted Parameters</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="overflow-x-auto"> | |
| <table className="w-full"> | |
| <thead> | |
| <tr className="border-b"> | |
| <th className="text-left py-2">Parameter</th> | |
| <th className="text-left py-2">Value</th> | |
| <th className="text-left py-2">Confidence</th> | |
| <th className="text-left py-2">Source</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {results.map((param, idx) => ( | |
| <tr key={idx} className="border-b"> | |
| <td className="py-2 font-mono">{param.param}</td> | |
| <td className="py-2">{JSON.stringify(param.value)}</td> | |
| <td className="py-2"> | |
| <div className="progress-container"> | |
| <div | |
| className="progress-bar" | |
| style={{ width: `${param.confidence * 100}%` }} | |
| /> | |
| </div> | |
| <span className="text-sm">{(param.confidence * 100).toFixed(1)}%</span> | |
| </td> | |
| <td className="py-2 text-sm text-gray-600">{param.source}</td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| </div> | |
| ); | |
| } |