feat: complete MVP epics E7-E10 (provisioning, console proxy, audit, frontend)
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { useState } from "react";
|
||||
import { createVM } from "../api";
|
||||
|
||||
interface Props {
|
||||
token: string;
|
||||
projectID: string;
|
||||
onCreated: () => void;
|
||||
}
|
||||
|
||||
export function VMCreate({ token, projectID, onCreated }: Props) {
|
||||
const [name, setName] = useState("");
|
||||
const [templateID, setTemplateID] = useState("");
|
||||
const [node, setNode] = useState("");
|
||||
const [vcpu, setVCPU] = useState(2);
|
||||
const [ramMB, setRAMMB] = useState(2048);
|
||||
const [diskGB, setDiskGB] = useState(10);
|
||||
const [sshKeyID, setSSHKeyID] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
async function handleCreate() {
|
||||
setError("");
|
||||
setBusy(true);
|
||||
try {
|
||||
await createVM(token, projectID, {
|
||||
name,
|
||||
template_id: templateID,
|
||||
node,
|
||||
vcpu,
|
||||
ram_mb: ramMB,
|
||||
disk_gb: diskGB,
|
||||
ssh_key_id: sshKeyID || undefined,
|
||||
});
|
||||
setName("");
|
||||
setTemplateID("");
|
||||
setOpen(false);
|
||||
onCreated();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Fehler");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
return (
|
||||
<button className="primary-button" onClick={() => setOpen(true)}>
|
||||
Neue VM erstellen
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="panel">
|
||||
<div className="panel-header">
|
||||
<div>
|
||||
<p className="eyebrow">Provisioning</p>
|
||||
<h2>VM erstellen</h2>
|
||||
</div>
|
||||
<button className="ghost-button" onClick={() => setOpen(false)}>
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
{error ? <p className="form-error">{error}</p> : null}
|
||||
<div className="metric-list">
|
||||
<label>
|
||||
Name
|
||||
<input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="meine-vm"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Template-ID
|
||||
<input
|
||||
value={templateID}
|
||||
onChange={(e) => setTemplateID(e.target.value)}
|
||||
placeholder="UUID des Templates"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Node
|
||||
<input
|
||||
value={node}
|
||||
onChange={(e) => setNode(e.target.value)}
|
||||
placeholder="pve"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
vCPU
|
||||
<input
|
||||
type="number"
|
||||
value={vcpu}
|
||||
onChange={(e) => setVCPU(Number(e.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
RAM (MB)
|
||||
<input
|
||||
type="number"
|
||||
value={ramMB}
|
||||
onChange={(e) => setRAMMB(Number(e.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Disk (GB)
|
||||
<input
|
||||
type="number"
|
||||
value={diskGB}
|
||||
onChange={(e) => setDiskGB(Number(e.target.value))}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
SSH-Key-ID (optional)
|
||||
<input
|
||||
value={sshKeyID}
|
||||
onChange={(e) => setSSHKeyID(e.target.value)}
|
||||
placeholder="UUID"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
className="primary-button"
|
||||
disabled={busy}
|
||||
onClick={handleCreate}
|
||||
>
|
||||
{busy ? "Erstelle..." : "VM erstellen"}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user