diff --git a/backend/db/postgres.go b/backend/db/postgres.go
index 85ad234..f411107 100644
--- a/backend/db/postgres.go
+++ b/backend/db/postgres.go
@@ -492,7 +492,7 @@ func (d *Database) writeMetrics(tx *sql.Tx, agentID string, ts time.Time, data *
}
func (d *Database) GetAgents() ([]models.Agent, error) {
- rows, err := d.db.Query("SELECT id, name, hostname, ip, opnsense_version, agent_version, platform, registered_at, last_heartbeat, customer_id, update_requested FROM agents ORDER BY name")
+ rows, err := d.db.Query("SELECT id, name, hostname, ip, opnsense_version, agent_version, platform, registered_at, last_heartbeat, customer_id, update_requested, last_api_key FROM agents ORDER BY name")
if err != nil {
return nil, err
}
@@ -504,7 +504,7 @@ func (d *Database) GetAgents() ([]models.Agent, error) {
var lastHB sql.NullTime
var custID sql.NullInt64
- if err := rows.Scan(&a.ID, &a.Name, &a.Hostname, &a.IP, &a.OPNsenseVersion, &a.AgentVersion, &a.Platform, &a.RegisteredAt, &lastHB, &custID, &a.UpdateRequested); err != nil {
+ if err := rows.Scan(&a.ID, &a.Name, &a.Hostname, &a.IP, &a.OPNsenseVersion, &a.AgentVersion, &a.Platform, &a.RegisteredAt, &lastHB, &custID, &a.UpdateRequested, &a.LastAPIKey); err != nil {
return nil, err
}
if lastHB.Valid {
diff --git a/frontend/src/components/ProxmoxPanel.jsx b/frontend/src/components/ProxmoxPanel.jsx
index 502f15c..bd3a597 100644
--- a/frontend/src/components/ProxmoxPanel.jsx
+++ b/frontend/src/components/ProxmoxPanel.jsx
@@ -11,32 +11,51 @@ import {
MonitorSmartphone, Terminal, Cable, ExternalLink, Plus, Unplug, Search, Shield, Play, FileCode,
} from 'lucide-react'
-const buildTabs = (hasPBS) => [
- { id: 'overview', label: 'Uebersicht' },
- { id: 'vms', label: 'Virtuelle Maschinen' },
- { id: 'containers', label: 'Container' },
- { id: 'storage', label: 'Storage' },
- { id: 'zfs', label: 'ZFS' },
- ...(hasPBS ? [{ id: 'pbs', label: 'Backup Server' }] : []),
- { id: 'tunnel', label: 'Tunnel' },
- { id: 'services', label: 'Dienste' },
- { id: 'updates', label: 'Updates' },
- { id: 'tasks', label: 'Aufgaben' },
- { id: 'backups', label: 'Backups' },
- { id: 'agent', label: 'Agent' },
+const mainCategories = [
+ { id: 'uebersicht', label: 'Uebersicht' },
+ { id: 'virtualisierung', label: 'Virtualisierung' },
+ { id: 'speicher', label: 'Speicher' },
+ { id: 'system', label: 'System' },
]
+const buildSubTabs = (hasPBS) => ({
+ virtualisierung: [
+ { id: 'vms', label: 'VMs' },
+ { id: 'containers', label: 'Container' },
+ ],
+ speicher: [
+ { id: 'storage', label: 'Storage' },
+ { id: 'zfs', label: 'ZFS' },
+ { id: 'backups', label: 'Backups' },
+ ...(hasPBS ? [{ id: 'pbs', label: 'Backup Server' }] : []),
+ ],
+ system: [
+ { id: 'services', label: 'Dienste' },
+ { id: 'updates', label: 'Updates' },
+ { id: 'tasks', label: 'Aufgaben' },
+ { id: 'tunnel', label: 'Tunnel' },
+ { id: 'agent', label: 'Agent' },
+ ],
+})
+
export default function ProxmoxPanel({ agentId, customers, onClose, onReload }) {
const [data, setData] = useState(null)
- const [tab, setTab] = useState('overview')
+ const [mainTab, setMainTab] = useState('uebersicht')
+ const [subTab, setSubTab] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
setLoading(true)
- setTab('overview')
+ setMainTab('uebersicht')
+ setSubTab(null)
api.getAgent(agentId).then(setData).finally(() => setLoading(false))
}, [agentId])
+ const handleMainTab = (id, subTabs) => {
+ setMainTab(id)
+ setSubTab(subTabs?.[id]?.[0]?.id ?? null)
+ }
+
useEffect(() => {
if (!data) return
const iv = setInterval(() => {
@@ -52,12 +71,32 @@ export default function ProxmoxPanel({ agentId, customers, onClose, onReload })
const proxmox = sys?.proxmox
const pbs = sys?.pbs
const cust = agent.customer_id ? customers.find((c) => c.id === agent.customer_id) : null
- const tabs = buildTabs(pbs?.available)
+ const subs = buildSubTabs(pbs?.available)
+ const currentSubs = mainTab !== 'uebersicht' ? (subs[mainTab] || []) : []
+ const activeSubTab = currentSubs.find(s => s.id === subTab)?.id ?? currentSubs[0]?.id
if (!proxmox?.available) {
return