(e: React.FormEvent)
| 457 | }; |
| 458 | |
| 459 | const handleSubmit = async (e: React.FormEvent) => { |
| 460 | e.preventDefault(); |
| 461 | setError(""); |
| 462 | |
| 463 | if (!name.trim()) { |
| 464 | setError("Please enter a plugin name"); |
| 465 | return; |
| 466 | } |
| 467 | |
| 468 | const ext = `.${fileType}`; |
| 469 | const fileName = name.endsWith(ext) ? name : `${name}${ext}`; |
| 470 | |
| 471 | if (!/^[a-zA-Z0-9_-]+\.(js|ts)$/.test(fileName)) { |
| 472 | setError("Name can only contain letters, numbers, hyphens, and underscores"); |
| 473 | return; |
| 474 | } |
| 475 | |
| 476 | try { |
| 477 | setLoading(true); |
| 478 | await savePlugin(fileName, content); |
| 479 | toast.success(`Created ${fileName}`); |
| 480 | resetForm(); |
| 481 | setOpen(false); |
| 482 | onSuccess(); |
| 483 | } catch (err: any) { |
| 484 | const msg = err.response?.data?.error || err.message || "Failed to create plugin"; |
| 485 | setError(msg); |
| 486 | } finally { |
| 487 | setLoading(false); |
| 488 | } |
| 489 | }; |
| 490 | |
| 491 | return ( |
| 492 | <Dialog open={open} onOpenChange={(o) => { setOpen(o); if (!o) resetForm(); }}> |
nothing calls this directly
no test coverage detected