feat: redesign responsive console and fix restore regressions
This commit is contained in:
+27
-10
@@ -1,12 +1,16 @@
|
||||
import { useEffect, useRef, useState, type FormEvent } from 'react';
|
||||
import { api, selectableImage, type Instance } from './api';
|
||||
import { ResourceState, useAction, useResource } from './ui';
|
||||
import { AlertBox } from './components/ui/alert';
|
||||
import { buttonVariants } from './components/ui/button';
|
||||
import { inputClass, selectClass, selectStyle } from './components/ui/input';
|
||||
import { cn } from './lib/utils';
|
||||
|
||||
export function CreateInstance({close, refresh}: {close: () => void; refresh: () => Promise<void>}) {
|
||||
const images = useResource(api.images), action = useAction();
|
||||
const [progress, setProgress] = useState('');
|
||||
const active = useRef(true);
|
||||
useEffect(() => {active.current = true; return () => {active.current = false;};}, []);
|
||||
useEffect(() => { active.current = true; return () => { active.current = false; }; }, []);
|
||||
const available = images.data?.filter(selectableImage) || [];
|
||||
async function submit(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
@@ -16,18 +20,31 @@ export function CreateInstance({close, refresh}: {close: () => void; refresh: ()
|
||||
if (!active.current) return;
|
||||
if (!image) throw new Error('请选择可用的本地镜像。');
|
||||
setProgress('正在创建实例,请勿重复提交…');
|
||||
const created = await api.request<Instance>('/instances', 'POST', {name, image_id:image.image_id || image.id});
|
||||
const created = await api.request<Instance>('/instances', 'POST', {name, image_id: image.image_id || image.id});
|
||||
if (!created?.id) throw new Error('未收到实例确认,请刷新实例列表核实,勿重复提交。');
|
||||
if (active.current) {await refresh(); close();}
|
||||
if (active.current) { await refresh(); close(); }
|
||||
});
|
||||
if (active.current) setProgress('');
|
||||
}
|
||||
return <form className="inline-form" onSubmit={submit}><h3>创建独立实例</h3>{action.feedback}
|
||||
<p className="callout">请选择已验证并准入的本地镜像。创建使用所选镜像的精确 ID,不会自动构建或切换版本。</p>
|
||||
<fieldset disabled={action.busy}><label>实例名称<input name="name" required maxLength={64}/></label>
|
||||
<ResourceState resource={images}/><label>本地镜像<select name="image" required defaultValue=""><option value="" disabled>选择已验证镜像</option>{available.map(i => <option key={i.image_id || i.id} value={i.image_id || i.id}>{i.version} · {i.tag || i.image_id || i.id}</option>)}</select></label>
|
||||
{!images.loading && !available.length && <p>暂无已准入的可用镜像。请联系管理员构建并验证发布镜像。</p>}
|
||||
<button className="primary" disabled={!available.length}>确认创建</button>
|
||||
</fieldset>{progress && <p role="status">{progress}</p>}<button type="button" disabled={action.busy} onClick={close}>关闭</button>
|
||||
return <form className="mb-5 grid gap-4 rounded-lg border border-border bg-muted/50 p-4" onSubmit={submit}>
|
||||
<h3 className="m-0 text-[14px] font-semibold">创建独立实例</h3>
|
||||
{action.feedback}
|
||||
<AlertBox variant="info">请选择已验证并准入的本地镜像。创建使用所选镜像的精确 ID,不会自动构建或切换版本。</AlertBox>
|
||||
<fieldset disabled={action.busy} className="m-0 grid min-w-0 gap-4 border-0 p-0">
|
||||
<label className="grid gap-1.5 text-[13px] font-medium">实例名称<input className={inputClass} name="name" required maxLength={64} /></label>
|
||||
<ResourceState resource={images} />
|
||||
<label className="grid gap-1.5 text-[13px] font-medium">本地镜像
|
||||
<select className={selectClass} style={selectStyle} name="image" required defaultValue="">
|
||||
<option value="" disabled>选择已验证镜像</option>
|
||||
{available.map(i => <option key={i.image_id || i.id} value={i.image_id || i.id}>{i.version} · {i.tag || i.image_id || i.id}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
{!images.loading && !available.length && <p className="m-0 text-[13px] text-muted-foreground">暂无已准入的可用镜像。请联系管理员构建并验证发布镜像。</p>}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button className={cn(buttonVariants())} disabled={!available.length}>确认创建</button>
|
||||
<button type="button" className={cn(buttonVariants({variant: 'ghost'}))} disabled={action.busy} onClick={close}>关闭</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
{progress && <p role="status" className="m-0 text-[13px] text-muted-foreground">{progress}</p>}
|
||||
</form>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user