feat(monitor): sign in to a PBS with user and password and add a Tasks tab

- Connect a PBS with user and password, creating a read-only API token
- Never store the password; remove the token when the role cannot be granted
- Show the PBS connection form as a dialog with the project contrast and blue actions
- Move recent tasks and maintenance schedules of a PBS to a new Tasks tab
- Keep the Datastores tab for datastores, disks and pools
This commit is contained in:
MacRimi
2026-10-19 23:12:31 +02:00
parent cb585632c6
commit 77a6d1c397
13 changed files with 282 additions and 61 deletions
+13 -4
View File
@@ -32,6 +32,7 @@ import {
CheckCircle,
XCircle,
Server,
ListChecks,
Menu,
LayoutDashboard,
HardDrive,
@@ -116,11 +117,11 @@ function PBSDashboard() {
}
type Product = "pve" | "pbs"
type TopTab = TabId | "datastores"
type TopTab = TabId | "datastores" | "pbs-tasks"
// Proxmox Backup Server has no guests, apps or PVE storage: it keeps the
// same header, health and administration with its own top-level sections.
const PBS_TABS: TopTab[] = ["overview", "datastores", "terminal", "admin"]
const PBS_TABS: TopTab[] = ["overview", "datastores", "pbs-tasks", "terminal", "admin"]
function MonitorDashboard({ product }: { product: Product }) {
const t = useT()
@@ -421,6 +422,7 @@ function MonitorDashboard({ product }: { product: Product }) {
switch (activeTab) {
case "overview": return t("navigation.overview")
case "datastores": return t("pbs.datastores")
case "pbs-tasks": return t("pbs.tasksTab")
case "apps": return t("navigation.apps")
case "vms": return t("navigation.virtualMachines")
case "storage": return t("navigation.storage")
@@ -717,6 +719,7 @@ function MonitorDashboard({ product }: { product: Product }) {
switch (id) {
case "overview": return renderDirect("overview", LayoutDashboard, t("navigation.overview"))
case "datastores": return renderDirect("datastores", Database, t("pbs.datastores"))
case "pbs-tasks": return renderDirect("pbs-tasks", ListChecks, t("pbs.tasksTab"))
case "apps": return renderDirect("apps", Grid3x3, t("navigation.apps"))
case "vms": return renderDirect("vms", Boxes, t("navigation.virtualMachines"))
case "backup": return renderDirect("backup", DatabaseBackup, t("navigation.backup"))
@@ -726,7 +729,7 @@ function MonitorDashboard({ product }: { product: Product }) {
}
}
return (
<TabsList className={`hidden lg:grid w-full ${isPbs ? "grid-cols-4" : "grid-cols-7"} bg-card border border-border`}>
<TabsList className={`hidden lg:grid w-full ${isPbs ? "grid-cols-5" : "grid-cols-7"} bg-card border border-border`}>
{tabOrder.map(renderTop)}
</TabsList>
)
@@ -788,6 +791,7 @@ function MonitorDashboard({ product }: { product: Product }) {
switch (id) {
case "overview": return btn("overview", LayoutDashboard, t("navigation.overview"))
case "datastores": return btn("datastores", Database, t("pbs.datastores"))
case "pbs-tasks": return btn("pbs-tasks", ListChecks, t("pbs.tasksTab"))
case "apps": return btn("apps", Grid3x3, t("navigation.apps"))
case "vms": return btn("vms", Boxes, t("navigation.virtualMachines"))
case "backup": return btn("backup", DatabaseBackup, t("navigation.backup"))
@@ -842,10 +846,15 @@ function MonitorDashboard({ product }: { product: Product }) {
{isPbs && (
<TabsContent value="datastores" className="space-y-4 md:space-y-6 mt-0">
<PBSPanel key={`datastores-${componentKey}`} localOnly />
<PBSPanel key={`datastores-${componentKey}`} localOnly view="datastores" />
<StorageOverview key={`datastore-disks-${componentKey}`} disksOnly />
</TabsContent>
)}
{isPbs && (
<TabsContent value="pbs-tasks" className="space-y-4 md:space-y-6 mt-0">
<PBSPanel key={`pbs-tasks-${componentKey}`} localOnly view="tasks" />
</TabsContent>
)}
{!isPbs && <>
<TabsContent value="apps" className="space-y-4 md:space-y-6 mt-0">