diff --git a/webui/src/components/InstanceDialog.tsx b/webui/src/components/InstanceDialog.tsx index 4a54f7a..fae8180 100644 --- a/webui/src/components/InstanceDialog.tsx +++ b/webui/src/components/InstanceDialog.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -12,6 +12,7 @@ import { BackendType, type CreateInstanceOptions, type Instance } from "@/types/ import ParseCommandDialog from "@/components/ParseCommandDialog"; import InstanceSettingsCard from "@/components/instance/InstanceSettingsCard"; import BackendConfigurationCard from "@/components/instance/BackendConfigurationCard"; +import { Upload } from "lucide-react"; interface InstanceDialogProps { open: boolean; @@ -32,6 +33,7 @@ const InstanceDialog: React.FC = ({ const [formData, setFormData] = useState({}); const [nameError, setNameError] = useState(""); const [showParseDialog, setShowParseDialog] = useState(false); + const fileInputRef = useRef(null); // Reset form when dialog opens/closes or when instance changes @@ -153,6 +155,49 @@ const InstanceDialog: React.FC = ({ setShowParseDialog(false); }; + const handleImportFile = () => { + fileInputRef.current?.click(); + }; + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const content = e.target?.result as string; + const importedData = JSON.parse(content); + + // Validate that it's an instance export + if (!importedData.name || !importedData.options) { + alert('Invalid instance file: Missing required fields (name, options)'); + return; + } + + // Set the instance name (only for new instances, not editing) + if (!isEditing) { + handleNameChange(importedData.name); + } + + // Populate all the options from the imported file + if (importedData.options) { + setFormData(prev => ({ + ...prev, + ...importedData.options, + })); + } + + // Reset the file input + event.target.value = ''; + } catch (error) { + console.error('Failed to parse instance file:', error); + alert(`Failed to parse instance file: ${error instanceof Error ? error.message : 'Invalid JSON'}`); + } + }; + + reader.readAsText(file); + }; // Save button label logic let saveButtonLabel = "Create Instance"; @@ -168,14 +213,38 @@ const InstanceDialog: React.FC = ({ - - {isEditing ? "Edit Instance" : "Create New Instance"} - - - {isEditing - ? "Modify the instance configuration below." - : "Configure your new llama-server instance below."} - +
+
+ + {isEditing ? "Edit Instance" : "Create New Instance"} + + + {isEditing + ? "Modify the instance configuration below." + : "Configure your new llama-server instance below."} + +
+ {!isEditing && ( + + )} +
+
diff --git a/webui/src/components/instance/InstanceSettingsCard.tsx b/webui/src/components/instance/InstanceSettingsCard.tsx index 87b32fb..1834eab 100644 --- a/webui/src/components/instance/InstanceSettingsCard.tsx +++ b/webui/src/components/instance/InstanceSettingsCard.tsx @@ -1,16 +1,14 @@ -import React, { useState, useEffect, useRef } from 'react' +import React, { useState, useEffect } from 'react' import type { CreateInstanceOptions } from '@/types/instance' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' -import { Button } from '@/components/ui/button' import AutoRestartConfiguration from '@/components/instance/AutoRestartConfiguration' import NumberInput from '@/components/form/NumberInput' import CheckboxInput from '@/components/form/CheckboxInput' import EnvironmentVariablesInput from '@/components/form/EnvironmentVariablesInput' import SelectInput from '@/components/form/SelectInput' import { nodesApi, type NodesMap } from '@/lib/api' -import { Upload } from 'lucide-react' interface InstanceSettingsCardProps { instanceName: string @@ -31,7 +29,6 @@ const InstanceSettingsCard: React.FC = ({ }) => { const [nodes, setNodes] = useState({}) const [loadingNodes, setLoadingNodes] = useState(true) - const fileInputRef = useRef(null) useEffect(() => { const fetchNodes = async () => { @@ -70,79 +67,12 @@ const InstanceSettingsCard: React.FC = ({ const selectedNode = formData.nodes && formData.nodes.length > 0 ? formData.nodes[0] : '' - const handleImportFile = () => { - fileInputRef.current?.click() - } - - const handleFileChange = (event: React.ChangeEvent) => { - const file = event.target.files?.[0] - if (!file) return - - const reader = new FileReader() - reader.onload = (e) => { - try { - const content = e.target?.result as string - const importedData = JSON.parse(content) - - // Validate that it's an instance export - if (!importedData.name || !importedData.options) { - alert('Invalid instance file: Missing required fields (name, options)') - return - } - - // Set the instance name (only for new instances, not editing) - if (!isEditing) { - onNameChange(importedData.name) - } - - // Populate all the options from the imported file - if (importedData.options) { - // Set all the options fields - Object.entries(importedData.options).forEach(([key, value]) => { - onChange(key as keyof CreateInstanceOptions, value) - }) - } - - // Reset the file input - event.target.value = '' - } catch (error) { - console.error('Failed to parse instance file:', error) - alert(`Failed to parse instance file: ${error instanceof Error ? error.message : 'Invalid JSON'}`) - } - } - - reader.readAsText(file) - } - return ( Instance Settings - {/* Import Instance Section - only show when creating new instance */} - {!isEditing && ( -
- - -
- )} - {/* Instance Name */}