feat: add start/stop/restart buttons for VMs and LXCs
- New POST /api/power route for Proxmox power operations - powerControl() method in proxmox.js library - Start/Stop/Restart action buttons in VMList and LXCList tables - Status feedback banner in Dashboard on power action Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import NodeList from './NodeList';
|
||||
import VMList from './VMList';
|
||||
import LXCList from './LXCList';
|
||||
import axios from 'axios';
|
||||
import { SWRConfig } from 'swr';
|
||||
|
||||
const POLL_INTERVAL = Number(process.env.NEXT_PUBLIC_API_POLL_INTERVAL || 30000);
|
||||
|
||||
@@ -30,6 +31,23 @@ async function fetchData(url) {
|
||||
/** @returns {import('react').ReactNode} */
|
||||
export default function Dashboard() {
|
||||
const [activeTab, setActiveTab] = useState('nodes');
|
||||
const [powerActionStatus, setPowerActionStatus] = useState(null);
|
||||
|
||||
/**
|
||||
* @param {string} node
|
||||
* @param {number} vmid
|
||||
* @param {string} type
|
||||
* @param {string} action
|
||||
*/
|
||||
const handlePowerAction = async (node, vmid, type, action) => {
|
||||
setPowerActionStatus({ text: `${action.charAt(0).toUpperCase() + action.slice(1)}ing ${type === 'qemu' ? 'VM' : 'LXC'} ${vmid}...`, type: 'pending' });
|
||||
try {
|
||||
await axios.post('/api/power', { node, vmid, type, action });
|
||||
setPowerActionStatus({ text: `${action.charAt(0).toUpperCase() + action.slice(1)} successful`, type: 'success' });
|
||||
} catch (err) {
|
||||
setPowerActionStatus({ text: err.message || `${action.charAt(0).toUpperCase() + action.slice(1)} failed`, type: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
const { data: nodesData, error: nodesError } = useSWR('/api/nodes', fetchData, {
|
||||
refreshInterval: POLL_INTERVAL,
|
||||
@@ -100,6 +118,16 @@ export default function Dashboard() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{powerActionStatus && (
|
||||
<div className={`mb-6 rounded-xl border p-4 text-sm ${
|
||||
powerActionStatus.type === 'success' ? 'border-status-green/30 bg-status-green/10 text-status-green'
|
||||
: powerActionStatus.type === 'error' ? 'border-status-red/30 bg-status-red/10 text-status-red'
|
||||
: 'border-border-card bg-bg-card text-text-secondary'
|
||||
}`}>
|
||||
{powerActionStatus.text}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tab navigation */}
|
||||
<div className="mb-4 flex gap-2 border-b border-border-card">
|
||||
{tabs.map((tab) => (
|
||||
@@ -119,8 +147,8 @@ export default function Dashboard() {
|
||||
{/* Tab content */}
|
||||
<div>
|
||||
{activeTab === 'nodes' && <NodeList nodes={nodes} />}
|
||||
{activeTab === 'vms' && <VMList vms={vms} />}
|
||||
{activeTab === 'lxc' && <LXCList lxc={lxc} />}
|
||||
{activeTab === 'vms' && <VMList vms={vms} onPowerAction={handlePowerAction} />}
|
||||
{activeTab === 'lxc' && <LXCList lxc={lxc} onPowerAction={handlePowerAction} />}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
|
||||
Reference in New Issue
Block a user