|
3 | 3 | import { useState, useEffect, useCallback, useRef } from 'react'; |
4 | 4 | import Navigation from '@/components/Navigation'; |
5 | 5 | import Toast, { useToast } from '@/components/Toast'; |
6 | | -import { CopyIcon, CheckIcon, CodeIcon, LinkIcon, ClockIcon, TrashIcon, ExternalLinkIcon } from '@/components/Icons'; |
| 6 | +import { CopyIcon, CheckIcon, CodeIcon, LinkIcon, ClockIcon, TrashIcon, ExternalLinkIcon, UploadIcon } from '@/components/Icons'; |
7 | 7 | import styles from './page.module.css'; |
8 | 8 |
|
9 | 9 | const STORAGE_KEY = 'json-bins-history'; |
@@ -68,6 +68,7 @@ export default function JsonServerPage() { |
68 | 68 | const [history, setHistory] = useState<JsonBin[]>([]); |
69 | 69 | const [copiedField, setCopiedField] = useState<string | null>(null); |
70 | 70 | const textareaRef = useRef<HTMLTextAreaElement>(null); |
| 71 | + const fileInputRef = useRef<HTMLInputElement>(null); |
71 | 72 | const { toasts, addToast, removeToast } = useToast(); |
72 | 73 |
|
73 | 74 | // Load history from localStorage |
@@ -172,6 +173,39 @@ export default function JsonServerPage() { |
172 | 173 | setResult(null); |
173 | 174 | }; |
174 | 175 |
|
| 176 | + const handleImportClick = () => { |
| 177 | + fileInputRef.current?.click(); |
| 178 | + }; |
| 179 | + |
| 180 | + const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { |
| 181 | + const file = event.target.files?.[0]; |
| 182 | + if (!file) return; |
| 183 | + |
| 184 | + const reader = new FileReader(); |
| 185 | + reader.onload = (e) => { |
| 186 | + const content = e.target?.result as string; |
| 187 | + try { |
| 188 | + // Try parsing to verify it's valid JSON, but allow editing even if slightly off (though requirements say valid json) |
| 189 | + // Actually the tool validates on input change, so we just set the text. |
| 190 | + // We'll throw if it's not even text. |
| 191 | + |
| 192 | + // Let's verify it's valid JSON for better UX before setting |
| 193 | + JSON.parse(content); |
| 194 | + setJsonInput(content); |
| 195 | + addToast('JSON imported successfully', 'success'); |
| 196 | + } catch (error) { |
| 197 | + addToast('Invalid JSON file', 'error'); |
| 198 | + } |
| 199 | + }; |
| 200 | + reader.onerror = () => { |
| 201 | + addToast('Error reading file', 'error'); |
| 202 | + }; |
| 203 | + reader.readAsText(file); |
| 204 | + |
| 205 | + // Reset input so same file can be selected again |
| 206 | + event.target.value = ''; |
| 207 | + }; |
| 208 | + |
175 | 209 | const handleCopy = async (text: string, field: string) => { |
176 | 210 | await navigator.clipboard.writeText(text); |
177 | 211 | setCopiedField(field); |
@@ -230,6 +264,21 @@ export default function JsonServerPage() { |
230 | 264 | {/* Toolbar */} |
231 | 265 | <div className={styles.toolbar}> |
232 | 266 | <div className={styles.toolbarLeft}> |
| 267 | + <input |
| 268 | + type="file" |
| 269 | + ref={fileInputRef} |
| 270 | + onChange={handleFileChange} |
| 271 | + accept="application/json" |
| 272 | + style={{ display: 'none' }} |
| 273 | + /> |
| 274 | + <button |
| 275 | + onClick={handleImportClick} |
| 276 | + className={styles.toolButton} |
| 277 | + title="Import JSON file" |
| 278 | + > |
| 279 | + <UploadIcon size={16} style={{ marginRight: '6px' }} /> |
| 280 | + Import |
| 281 | + </button> |
233 | 282 | <button |
234 | 283 | onClick={handleFormat} |
235 | 284 | disabled={!validation.valid} |
|
0 commit comments