Move import instance configuration to InstanceDialog component

This commit is contained in:
2025-10-27 20:17:18 +01:00
parent a00c9b82a6
commit 7813a5f2be
2 changed files with 79 additions and 80 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@@ -12,6 +12,7 @@ import { BackendType, type CreateInstanceOptions, type Instance } from "@/types/
import ParseCommandDialog from "@/components/ParseCommandDialog"; import ParseCommandDialog from "@/components/ParseCommandDialog";
import InstanceSettingsCard from "@/components/instance/InstanceSettingsCard"; import InstanceSettingsCard from "@/components/instance/InstanceSettingsCard";
import BackendConfigurationCard from "@/components/instance/BackendConfigurationCard"; import BackendConfigurationCard from "@/components/instance/BackendConfigurationCard";
import { Upload } from "lucide-react";
interface InstanceDialogProps { interface InstanceDialogProps {
open: boolean; open: boolean;
@@ -32,6 +33,7 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
const [formData, setFormData] = useState<CreateInstanceOptions>({}); const [formData, setFormData] = useState<CreateInstanceOptions>({});
const [nameError, setNameError] = useState(""); const [nameError, setNameError] = useState("");
const [showParseDialog, setShowParseDialog] = useState(false); const [showParseDialog, setShowParseDialog] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
// Reset form when dialog opens/closes or when instance changes // Reset form when dialog opens/closes or when instance changes
@@ -153,6 +155,49 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
setShowParseDialog(false); setShowParseDialog(false);
}; };
const handleImportFile = () => {
fileInputRef.current?.click();
};
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
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 // Save button label logic
let saveButtonLabel = "Create Instance"; let saveButtonLabel = "Create Instance";
@@ -168,6 +213,8 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px] max-h-[80vh] overflow-hidden flex flex-col"> <DialogContent className="sm:max-w-[600px] max-h-[80vh] overflow-hidden flex flex-col">
<DialogHeader> <DialogHeader>
<div className="flex items-center justify-between">
<div className="flex-1">
<DialogTitle> <DialogTitle>
{isEditing ? "Edit Instance" : "Create New Instance"} {isEditing ? "Edit Instance" : "Create New Instance"}
</DialogTitle> </DialogTitle>
@@ -176,6 +223,28 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
? "Modify the instance configuration below." ? "Modify the instance configuration below."
: "Configure your new llama-server instance below."} : "Configure your new llama-server instance below."}
</DialogDescription> </DialogDescription>
</div>
{!isEditing && (
<Button
type="button"
variant="ghost"
size="sm"
onClick={handleImportFile}
title="Import instance configuration from JSON file"
className="ml-2"
>
<Upload className="h-4 w-4 mr-2" />
Import
</Button>
)}
</div>
<input
ref={fileInputRef}
type="file"
accept=".json"
onChange={handleFileChange}
className="hidden"
/>
</DialogHeader> </DialogHeader>
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">

View File

@@ -1,16 +1,14 @@
import React, { useState, useEffect, useRef } from 'react' import React, { useState, useEffect } from 'react'
import type { CreateInstanceOptions } from '@/types/instance' import type { CreateInstanceOptions } from '@/types/instance'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Label } from '@/components/ui/label' import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import AutoRestartConfiguration from '@/components/instance/AutoRestartConfiguration' import AutoRestartConfiguration from '@/components/instance/AutoRestartConfiguration'
import NumberInput from '@/components/form/NumberInput' import NumberInput from '@/components/form/NumberInput'
import CheckboxInput from '@/components/form/CheckboxInput' import CheckboxInput from '@/components/form/CheckboxInput'
import EnvironmentVariablesInput from '@/components/form/EnvironmentVariablesInput' import EnvironmentVariablesInput from '@/components/form/EnvironmentVariablesInput'
import SelectInput from '@/components/form/SelectInput' import SelectInput from '@/components/form/SelectInput'
import { nodesApi, type NodesMap } from '@/lib/api' import { nodesApi, type NodesMap } from '@/lib/api'
import { Upload } from 'lucide-react'
interface InstanceSettingsCardProps { interface InstanceSettingsCardProps {
instanceName: string instanceName: string
@@ -31,7 +29,6 @@ const InstanceSettingsCard: React.FC<InstanceSettingsCardProps> = ({
}) => { }) => {
const [nodes, setNodes] = useState<NodesMap>({}) const [nodes, setNodes] = useState<NodesMap>({})
const [loadingNodes, setLoadingNodes] = useState(true) const [loadingNodes, setLoadingNodes] = useState(true)
const fileInputRef = useRef<HTMLInputElement>(null)
useEffect(() => { useEffect(() => {
const fetchNodes = async () => { const fetchNodes = async () => {
@@ -70,79 +67,12 @@ const InstanceSettingsCard: React.FC<InstanceSettingsCardProps> = ({
const selectedNode = formData.nodes && formData.nodes.length > 0 ? formData.nodes[0] : '' const selectedNode = formData.nodes && formData.nodes.length > 0 ? formData.nodes[0] : ''
const handleImportFile = () => {
fileInputRef.current?.click()
}
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
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 ( return (
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Instance Settings</CardTitle> <CardTitle>Instance Settings</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-6"> <CardContent className="space-y-6">
{/* Import Instance Section - only show when creating new instance */}
{!isEditing && (
<div className="pb-4 border-b">
<Button
type="button"
variant="outline"
onClick={handleImportFile}
size="sm"
title="Import instance configuration from JSON file"
>
<Upload className="h-4 w-4 mr-2" />
Import
</Button>
<input
ref={fileInputRef}
type="file"
accept=".json"
onChange={handleFileChange}
className="hidden"
/>
</div>
)}
{/* Instance Name */} {/* Instance Name */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="name"> <Label htmlFor="name">