import React, { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/index'; import { RefreshCw, Plus } from 'lucide-react'; import { BackupJobList } from '@/components/Proxmox'; import { listProxmoxClusters, listProxmoxBackupJobs } from '@/lib/proxmoxClient'; import type { ClusterInfo } from '@/lib/domain'; import { toast } from 'sonner'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/index'; import { Input } from '@/components/ui/index'; import { Label } from '@/components/ui/index'; export function ProxmoxBackupPage() { const [clusters, setClusters] = useState([]); const [selectedClusterId, setSelectedClusterId] = useState(''); // eslint-disable-next-line @typescript-eslint/no-explicit-any const [jobs, setJobs] = useState([]); const [isLoading, setIsLoading] = useState(false); const [showNewJobDialog, setShowNewJobDialog] = useState(false); // New job form state const [jobName, setJobName] = useState(''); const [jobNode, setJobNode] = useState(''); const [jobSchedule, setJobSchedule] = useState(''); const [jobVms, setJobVms] = useState(''); useEffect(() => { listProxmoxClusters() .then((cls) => { setClusters(cls); if (cls.length > 0) setSelectedClusterId(cls[0].id); }) .catch((err) => { console.error('Failed to load clusters:', err); toast.error('Failed to load clusters'); }); }, []); const loadJobs = useCallback(async (clusterId: string) => { if (!clusterId) return; setIsLoading(true); try { const raw = await listProxmoxBackupJobs(clusterId, ''); // eslint-disable-next-line @typescript-eslint/no-explicit-any const normalized = (raw as any[]).map((job) => { const enabledRaw = job.enabled ?? job.enable ?? 1; const isEnabled = enabledRaw === 1 || enabledRaw === true || enabledRaw === '1'; const nextRunRaw = job['next-run'] ?? job.next_run ?? job.nextRun; const nextRunStr = nextRunRaw ? new Date(Number(nextRunRaw) * 1000).toLocaleString() : undefined; return { id: job.id || String(job.jobid || ''), name: job.id || job.comment || `job-${job.jobid || '?'}`, node: job.node || 'all', schedule: job.schedule || '-', status: isEnabled ? ('idle' as const) : ('idle' as const), lastRun: undefined, nextRun: nextRunStr, size: undefined, count: undefined, enabled: isEnabled, vmid: job.vmid, storage: job.storage, mode: job.mode, compress: job.compress, comment: job.comment, }; }); setJobs(normalized); } catch (err) { console.error('Failed to load backup jobs:', err); toast.error('Failed to load backup jobs'); } finally { setIsLoading(false); } }, []); useEffect(() => { if (selectedClusterId) loadJobs(selectedClusterId); }, [selectedClusterId, loadJobs]); const handleNewJob = () => { setJobName(''); setJobNode(''); setJobSchedule(''); setJobVms(''); setShowNewJobDialog(true); }; const handleSubmitNewJob = async () => { if (!jobName || !jobNode || !jobSchedule) { toast.error('Job name, node, and schedule are required'); return; } try { toast.info(`Creating backup job ${jobName} - implementation pending`); setShowNewJobDialog(false); } catch (error) { console.error('Failed to create backup job:', error); toast.error(`Failed to create backup job: ${error}`); } }; if (clusters.length === 0 && !isLoading) { return (

Backup Jobs

Manage Proxmox backup schedules

No Proxmox clusters configured.

Add a remote connection first.

); } return (

Backup Jobs

Manage Proxmox backup schedules

{clusters.length > 1 && ( )}
loadJobs(selectedClusterId)} /> Create New Backup Job
setJobName(e.target.value)} placeholder="daily-backup" />
setJobNode(e.target.value)} placeholder="pve" />
setJobSchedule(e.target.value)} placeholder="0 2 * * *" />

Example: "0 2 * * *" for daily at 2:00 AM

setJobVms(e.target.value)} placeholder="100, 101, 102" />
); }