diff options
Diffstat (limited to 'frontend/src/components/modals')
| -rw-r--r-- | frontend/src/components/modals/JsonUploadModal.tsx | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/frontend/src/components/modals/JsonUploadModal.tsx b/frontend/src/components/modals/JsonUploadModal.tsx new file mode 100644 index 0000000..1cb541b --- /dev/null +++ b/frontend/src/components/modals/JsonUploadModal.tsx @@ -0,0 +1,161 @@ +import { useState, useRef } from 'react'; + +interface JsonUploadModalProps { + isOpen: boolean; + onClose: () => void; + onUpload: (data: any) => void; + game: string; +} + +const JsonUploadModal = ({ isOpen, onClose, onUpload, game }: JsonUploadModalProps) => { + const [file, setFile] = useState<File | null>(null); + const [error, setError] = useState<string>(''); + const [isLoading, setIsLoading] = useState(false); + const fileInputRef = useRef<HTMLInputElement>(null); + + if (!isOpen) return null; + + const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const selectedFile = e.target.files?.[0]; + setError(''); + + if (selectedFile) { + if (!selectedFile.name.endsWith('.json')) { + setError('Please select a JSON file'); + return; + } + setFile(selectedFile); + } + }; + + const handleUpload = async () => { + if (!file) { + setError('Please select a file to upload'); + return; + } + + setIsLoading(true); + setError(''); + + try { + const text = await file.text(); + const data = JSON.parse(text); + onUpload(data); + setFile(null); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + onClose(); + } catch (err) { + if (err instanceof SyntaxError) { + setError('Invalid JSON file format'); + } else { + setError('Failed to process file. Please try again.'); + } + } finally { + setIsLoading(false); + } + }; + + const handleClose = () => { + setFile(null); + setError(''); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + onClose(); + }; + + return ( + <div className="fixed inset-0 z-50 overflow-y-auto"> + {/* Backdrop */} + <div + className="fixed inset-0 bg-black/50 backdrop-blur-sm transition-opacity" + onClick={handleClose} + /> + + {/* Modal */} + <div className="flex min-h-full items-center justify-center p-4"> + <div className="relative bg-slate-900 rounded-lg border border-slate-700 w-full max-w-md p-6 shadow-xl"> + {/* Header */} + <div className="mb-6"> + <h3 className="text-xl font-bold text-white mb-2">Import {game} Data</h3> + <p className="text-slate-400 text-sm">Upload your game data in JSON format</p> + </div> + + {/* File Upload Area */} + <div className="mb-6"> + <label + htmlFor="file-upload" + className="relative block w-full rounded-lg border-2 border-dashed border-slate-600 p-12 text-center hover:border-violet-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-violet-500 focus-within:ring-offset-2 focus-within:ring-offset-slate-900 transition-colors cursor-pointer" + > + <svg + className="mx-auto h-12 w-12 text-slate-400" + stroke="currentColor" + fill="none" + viewBox="0 0 48 48" + > + <path + d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" + strokeWidth={2} + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + <span className="mt-2 block text-sm font-medium text-slate-300"> + {file ? file.name : 'Click to upload or drag and drop'} + </span> + <span className="mt-1 block text-xs text-slate-500">JSON files only</span> + <input + ref={fileInputRef} + id="file-upload" + name="file-upload" + type="file" + className="sr-only" + accept=".json" + onChange={handleFileChange} + /> + </label> + </div> + + {/* Error Message */} + {error && ( + <div className="mb-6 rounded-md bg-red-500/10 border border-red-500/20 p-3"> + <p className="text-sm text-red-400">{error}</p> + </div> + )} + + {/* Actions */} + <div className="flex space-x-3"> + <button + onClick={handleClose} + className="flex-1 border border-slate-600 hover:border-violet-500 text-slate-300 hover:text-white py-2 px-4 rounded-md font-medium transition-colors" + disabled={isLoading} + > + Cancel + </button> + <button + onClick={handleUpload} + className="flex-1 bg-violet-600 hover:bg-violet-700 text-white py-2 px-4 rounded-md font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed" + disabled={!file || isLoading} + > + {isLoading ? ( + <span className="flex items-center justify-center"> + <svg className="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24"> + <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" /> + <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" /> + </svg> + Processing... + </span> + ) : ( + 'Upload' + )} + </button> + </div> + </div> + </div> + </div> + ); +}; + +export default JsonUploadModal; |
