Refactor instance status handling on the frontend

This commit is contained in:
2025-08-27 20:11:21 +02:00
parent b41ebdc604
commit a8f3a8e0f5
12 changed files with 79 additions and 59 deletions

View File

@@ -27,6 +27,8 @@ const HealthBadge: React.FC<HealthBadgeProps> = ({ health }) => {
return <XCircle className="h-3 w-3" />;
case "unknown":
return <Loader2 className="h-3 w-3 animate-spin" />;
case "failed":
return <XCircle className="h-3 w-3" />;
}
};
@@ -40,6 +42,8 @@ const HealthBadge: React.FC<HealthBadgeProps> = ({ health }) => {
return "destructive";
case "unknown":
return "secondary";
case "failed":
return "destructive";
}
};
@@ -53,6 +57,8 @@ const HealthBadge: React.FC<HealthBadgeProps> = ({ health }) => {
return "Error";
case "unknown":
return "Unknown";
case "failed":
return "Failed";
}
};

View File

@@ -24,7 +24,7 @@ function InstanceCard({
editInstance,
}: InstanceCardProps) {
const [isLogsOpen, setIsLogsOpen] = useState(false);
const health = useInstanceHealth(instance.name, instance.running);
const health = useInstanceHealth(instance.name, instance.status);
const handleStart = () => {
startInstance(instance.name);
@@ -50,13 +50,15 @@ function InstanceCard({
setIsLogsOpen(true);
};
const running = instance.status === "running";
return (
<>
<Card>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">{instance.name}</CardTitle>
{instance.running && <HealthBadge health={health} />}
{running && <HealthBadge health={health} />}
</div>
</CardHeader>
@@ -66,7 +68,7 @@ function InstanceCard({
size="sm"
variant="outline"
onClick={handleStart}
disabled={instance.running}
disabled={running}
title="Start instance"
data-testid="start-instance-button"
>
@@ -77,7 +79,7 @@ function InstanceCard({
size="sm"
variant="outline"
onClick={handleStop}
disabled={!instance.running}
disabled={!running}
title="Stop instance"
data-testid="stop-instance-button"
>
@@ -108,7 +110,7 @@ function InstanceCard({
size="sm"
variant="destructive"
onClick={handleDelete}
disabled={instance.running}
disabled={running}
title="Delete instance"
data-testid="delete-instance-button"
>
@@ -122,7 +124,7 @@ function InstanceCard({
open={isLogsOpen}
onOpenChange={setIsLogsOpen}
instanceName={instance.name}
isRunning={instance.running}
isRunning={running}
/>
</>
);

View File

@@ -29,7 +29,6 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
instance,
}) => {
const isEditing = !!instance;
const isRunning = instance?.running || true; // Assume running if instance exists
const [instanceName, setInstanceName] = useState("");
const [formData, setFormData] = useState<CreateInstanceOptions>({});
@@ -114,6 +113,16 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
// Check if auto_restart is enabled
const isAutoRestartEnabled = formData.auto_restart === true;
// Save button label logic
let saveButtonLabel = "Create Instance";
if (isEditing) {
if (instance?.status === "running") {
saveButtonLabel = "Update & Restart Instance";
} else {
saveButtonLabel = "Update Instance";
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px] max-h-[80vh] overflow-hidden flex flex-col">
@@ -264,11 +273,7 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
disabled={!instanceName.trim() || !!nameError}
data-testid="dialog-save-button"
>
{isEditing
? isRunning
? "Update & Restart Instance"
: "Update Instance"
: "Create Instance"}
{saveButtonLabel}
</Button>
</DialogFooter>
</DialogContent>

View File

@@ -17,13 +17,13 @@ describe('InstanceCard - Instance Actions and State', () => {
const stoppedInstance: Instance = {
name: 'test-instance',
running: false,
status: 'stopped',
options: { model: 'test-model.gguf' }
}
const runningInstance: Instance = {
name: 'running-instance',
running: true,
status: 'running',
options: { model: 'running-model.gguf' }
}
@@ -301,7 +301,7 @@ afterEach(() => {
it('handles instance with minimal data', () => {
const minimalInstance: Instance = {
name: 'minimal',
running: false,
status: 'stopped',
options: {}
}
@@ -323,7 +323,7 @@ afterEach(() => {
it('handles instance with undefined options', () => {
const instanceWithoutOptions: Instance = {
name: 'no-options',
running: true,
status: 'running',
options: undefined
}

View File

@@ -44,9 +44,9 @@ describe('InstanceList - State Management and UI Logic', () => {
const mockEditInstance = vi.fn()
const mockInstances: Instance[] = [
{ name: 'instance-1', running: false, options: { model: 'model1.gguf' } },
{ name: 'instance-2', running: true, options: { model: 'model2.gguf' } },
{ name: 'instance-3', running: false, options: { model: 'model3.gguf' } }
{ name: 'instance-1', status: 'stopped', options: { model: 'model1.gguf' } },
{ name: 'instance-2', status: 'running', options: { model: 'model2.gguf' } },
{ name: 'instance-3', status: 'stopped', options: { model: 'model3.gguf' } }
]
const DUMMY_API_KEY = 'test-api-key-123'

View File

@@ -134,7 +134,7 @@ afterEach(() => {
describe('Edit Mode', () => {
const mockInstance: Instance = {
name: 'existing-instance',
running: false,
status: 'stopped',
options: {
model: 'test-model.gguf',
gpu_layers: 10,
@@ -184,8 +184,8 @@ afterEach(() => {
})
it('shows correct button text for running vs stopped instances', () => {
const runningInstance: Instance = { ...mockInstance, running: true }
const runningInstance: Instance = { ...mockInstance, status: 'running' }
const { rerender } = render(
<InstanceDialog
open={true}