added to remote repo
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { ArrowLeft, Play, RefreshCw } from 'lucide-react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { api, errorMessage } from '../api.js';
|
||||
import { JobStatusPanel } from './JobStatusPanel.jsx';
|
||||
import { RestoreModal } from './RestoreModal.jsx';
|
||||
import { SnapshotTable } from './SnapshotTable.jsx';
|
||||
|
||||
export function VMDetail({ jobs, onBack, onChanged, vm }) {
|
||||
const [snapshots, setSnapshots] = useState([]);
|
||||
const [selectedSnapshot, setSelectedSnapshot] = useState(null);
|
||||
const [confirmText, setConfirmText] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const vmJobs = useMemo(() => jobs.filter((job) => job.vmName === vm.name), [jobs, vm.name]);
|
||||
const visibleJob = vmJobs.find((job) => ['queued', 'running'].includes(job.status)) || vmJobs[0] || null;
|
||||
const latestSnapshot = snapshots[0]?.time;
|
||||
|
||||
async function loadSnapshots() {
|
||||
setError('');
|
||||
try {
|
||||
const result = await api.get(`/snapshots/${encodeURIComponent(vm.name)}`);
|
||||
setSnapshots(result.data);
|
||||
} catch (requestError) {
|
||||
setError(errorMessage(requestError));
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadSnapshots();
|
||||
}, [vm.name]);
|
||||
|
||||
async function createBackup() {
|
||||
setBusy(true);
|
||||
setError('');
|
||||
try {
|
||||
await api.post(`/backup/${encodeURIComponent(vm.name)}`);
|
||||
await onChanged();
|
||||
} catch (requestError) {
|
||||
setError(errorMessage(requestError));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmRestore() {
|
||||
setBusy(true);
|
||||
setError('');
|
||||
try {
|
||||
await api.post(`/restore/${encodeURIComponent(vm.name)}`, {
|
||||
snapshotId: selectedSnapshot.id,
|
||||
confirmVmName: confirmText,
|
||||
});
|
||||
setSelectedSnapshot(null);
|
||||
setConfirmText('');
|
||||
await onChanged();
|
||||
} catch (requestError) {
|
||||
setError(errorMessage(requestError));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="space-y-5">
|
||||
<div className="flex flex-col gap-4 border-b border-zinc-800 pb-5 sm:flex-row sm:items-end sm:justify-between">
|
||||
<div>
|
||||
<button className="mb-4 inline-flex items-center gap-2 text-sm text-zinc-400 hover:text-zinc-100" onClick={onBack} type="button">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
Back
|
||||
</button>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-2xl font-semibold text-zinc-100">{vm.name}</h1>
|
||||
<span className="rounded-md border border-zinc-800 px-2 py-1 text-xs text-zinc-300">{vm.status || 'Unknown'}</span>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-zinc-500">Latest snapshot: {latestSnapshot ? formatTime(latestSnapshot) : 'None'}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-700 px-3 text-sm text-zinc-100" onClick={loadSnapshots} type="button">
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
Snapshots
|
||||
</button>
|
||||
<button
|
||||
className="inline-flex h-9 items-center gap-2 rounded-md border border-emerald-800 bg-emerald-950 px-3 text-sm text-emerald-100 hover:bg-emerald-900"
|
||||
disabled={busy || Boolean(visibleJob && ['queued', 'running'].includes(visibleJob.status))}
|
||||
onClick={createBackup}
|
||||
type="button"
|
||||
>
|
||||
<Play className="h-4 w-4" />
|
||||
Create Backup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error ? <div className="rounded-md border border-amber-900 bg-amber-950/50 px-4 py-3 text-sm text-amber-200">{error}</div> : null}
|
||||
|
||||
<JobStatusPanel job={visibleJob} />
|
||||
<SnapshotTable snapshots={snapshots} onRestore={(snapshot) => setSelectedSnapshot(snapshot)} />
|
||||
<RestoreModal
|
||||
busy={busy}
|
||||
confirmText={confirmText}
|
||||
onCancel={() => {
|
||||
setSelectedSnapshot(null);
|
||||
setConfirmText('');
|
||||
}}
|
||||
onConfirm={confirmRestore}
|
||||
onTextChange={setConfirmText}
|
||||
snapshot={selectedSnapshot}
|
||||
vmName={vm.name}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function formatTime(value) {
|
||||
return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'medium' }).format(new Date(value));
|
||||
}
|
||||
Reference in New Issue
Block a user