public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements
@ 2025-04-11 14:05 Dominik Csapak
  2025-04-11 14:05 ` [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status Dominik Csapak
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-04-11 14:05 UTC (permalink / raw)
  To: pdm-devel

Some smaller improvements for the ui, mostly refactor and preparation
for an upcoming series that improves the search box.

pdm patch 3/3 depends on the proxmox-yew-comp patch.

proxmox-yew-comp:

Dominik Csapak (1):
  status: implement PartialEq and Clone for Status

 src/status.rs | 4 ++++
 1 file changed, 4 insertions(+)

proxmox-datacenter-manager:

Dominik Csapak (3):
  ui: improve keyboard navigation for search box
  ui: dashboard: refactor remotes panel
  ui: dashboard: refactor guest panel

 ui/src/dashboard/guest_panel.rs  | 143 +++++++++++++++++++++++++++++++
 ui/src/dashboard/mod.rs          | 103 ++++++----------------
 ui/src/dashboard/remote_panel.rs |  90 +++++++++++++++++++
 ui/src/pve/mod.rs                |  11 ++-
 ui/src/widget/resource_tree.rs   |  76 ++++++++++------
 5 files changed, 318 insertions(+), 105 deletions(-)
 create mode 100644 ui/src/dashboard/guest_panel.rs
 create mode 100644 ui/src/dashboard/remote_panel.rs

-- 
2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status
  2025-04-11 14:05 [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements Dominik Csapak
@ 2025-04-11 14:05 ` Dominik Csapak
  2025-04-14 14:50   ` Wolfgang Bumiller
  2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 1/3] ui: improve keyboard navigation for search box Dominik Csapak
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Dominik Csapak @ 2025-04-11 14:05 UTC (permalink / raw)
  To: pdm-devel

this is sensible to have, e.g. when we want to have the status structs
as parto of a type for a store

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/status.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/status.rs b/src/status.rs
index 9d951c5..ab77a15 100644
--- a/src/status.rs
+++ b/src/status.rs
@@ -7,6 +7,7 @@ use pwt::{
 
 /// Used to represent a Status of some resource or component, e.g.
 /// if a PVE node is online or not.
+#[derive(PartialEq, Clone)]
 pub enum Status {
     Success,
     Warning,
@@ -27,6 +28,7 @@ impl Status {
 }
 
 /// Used to represent the state of a Node, being PVE or PBS
+#[derive(PartialEq, Clone)]
 pub enum NodeState {
     Online,
     Offline,
@@ -45,6 +47,7 @@ impl NodeState {
 }
 
 /// Used to represent the state of a PVE guest, such as a VM
+#[derive(PartialEq, Clone)]
 pub enum GuestState {
     Running,
     Paused,
@@ -67,6 +70,7 @@ impl GuestState {
 }
 
 /// Used to represent the state of a Storage or Datastore
+#[derive(PartialEq, Clone)]
 pub enum StorageState {
     Available,
     Unavailable,
-- 
2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pdm-devel] [PATCH datacenter-manager 1/3] ui: improve keyboard navigation for search box
  2025-04-11 14:05 [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements Dominik Csapak
  2025-04-11 14:05 ` [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status Dominik Csapak
@ 2025-04-11 14:05 ` Dominik Csapak
  2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 2/3] ui: dashboard: refactor remotes panel Dominik Csapak
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-04-11 14:05 UTC (permalink / raw)
  To: pdm-devel

instead of triggering a navigation on selection change, trigger it only
on click and on pressing enter. With this, navigation in the tree is
possible without changing the current route.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 ui/src/widget/resource_tree.rs | 76 ++++++++++++++++++++++------------
 1 file changed, 50 insertions(+), 26 deletions(-)

diff --git a/ui/src/widget/resource_tree.rs b/ui/src/widget/resource_tree.rs
index 1b764f7..feff308 100644
--- a/ui/src/widget/resource_tree.rs
+++ b/ui/src/widget/resource_tree.rs
@@ -12,7 +12,10 @@ use pwt::{
     props::ExtractPrimaryKey,
     state::{Selection, TreeStore},
     widget::{
-        data_table::{DataTable, DataTableColumn, DataTableHeader},
+        data_table::{
+            DataTable, DataTableColumn, DataTableHeader, DataTableKeyboardEvent,
+            DataTableMouseEvent,
+        },
         ActionIcon, Column, Container, Fa, Panel, Progress, Row, Tooltip,
     },
 };
@@ -118,28 +121,8 @@ impl Component for PdmResourceTree {
         }
 
         let store = TreeStore::new().view_root(false);
-        let selection = Selection::new().on_select({
-            let store = store.clone();
-            let link = ctx.link().clone();
-            move |selection: Selection| {
-                let store = store.read();
-                let root = store.root().unwrap();
-
-                if let Some(key) = selection.selected_key() {
-                    if let Some(node) = root.find_node_by_key(&key) {
-                        match node.record() {
-                            PdmTreeEntry::Resource(remote, resource) => {
-                                crate::navigate_to(&link, remote, Some(resource));
-                            }
-                            PdmTreeEntry::Remote(remote, _) => {
-                                crate::navigate_to(&link, remote, None);
-                            }
-                            _ => {}
-                        }
-                    }
-                }
-            }
-        });
+        let selection = Selection::new();
+
         Self {
             loading: false,
             last_error: None,
@@ -231,8 +214,36 @@ impl Component for PdmResourceTree {
 
     fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
         let props = ctx.props();
-        let table = DataTable::new(columns(&ctx.link(), self.store.clone()), self.store.clone())
+        let table = DataTable::new(columns(ctx.link(), self.store.clone()), self.store.clone())
             .selection(self.selection.clone())
+            .on_row_click({
+                let store = self.store.clone();
+                let link = ctx.link().clone();
+                move |event: &mut DataTableMouseEvent| {
+                    let store = store.read();
+                    let root = store.root().unwrap();
+
+                    if let Some(node) = root.find_node_by_key(&event.record_key) {
+                        navigate_to_entry(&link, node.record());
+                    }
+                }
+            })
+            .on_row_keydown({
+                let store = self.store.clone();
+                let link = ctx.link().clone();
+                move |event: &mut DataTableKeyboardEvent| {
+                    let store = store.read();
+                    let root = store.root().unwrap();
+
+                    if event.key().as_str() != "Enter" {
+                        return;
+                    }
+
+                    if let Some(node) = root.find_node_by_key(&event.record_key) {
+                        navigate_to_entry(&link, node.record());
+                    }
+                }
+            })
             .class(FlexFit)
             .hover(true)
             .borderless(true);
@@ -275,6 +286,18 @@ impl Component for PdmResourceTree {
     }
 }
 
+fn navigate_to_entry(link: &html::Scope<PdmResourceTree>, record: &PdmTreeEntry) {
+    match record {
+        PdmTreeEntry::Root => {}
+        PdmTreeEntry::Resource(remote, resource) => {
+            crate::navigate_to(link, remote, Some(resource));
+        }
+        PdmTreeEntry::Remote(remote, _) => {
+            crate::navigate_to(link, remote, None);
+        }
+    }
+}
+
 fn columns(
     link: &html::Scope<PdmResourceTree>,
     store: TreeStore<PdmTreeEntry>,
@@ -316,14 +339,15 @@ fn columns(
                     .into()
             })
             .into(),
-        DataTableColumn::new(tr!("Node"))
+        DataTableColumn::new(tr!("Node/Error"))
             .flex(2)
             .render(|item: &PdmTreeEntry| {
                 match item {
                     PdmTreeEntry::Root => "",
                     PdmTreeEntry::Resource(_, resource) => {
-                        get_resource_node(&resource).unwrap_or("")
+                        get_resource_node(resource).unwrap_or("")
                     }
+                    PdmTreeEntry::Remote(_, Some(err)) => err,
                     PdmTreeEntry::Remote(_, _) => "",
                 }
                 .into()
-- 
2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pdm-devel] [PATCH datacenter-manager 2/3] ui: dashboard: refactor remotes panel
  2025-04-11 14:05 [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements Dominik Csapak
  2025-04-11 14:05 ` [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status Dominik Csapak
  2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 1/3] ui: improve keyboard navigation for search box Dominik Csapak
@ 2025-04-11 14:05 ` Dominik Csapak
  2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 3/3] ui: dashboard: refactor guest panel Dominik Csapak
  2025-04-16  9:01 ` [pdm-devel] applied-series: [PATCH yew-comp/datacenter-manager] some ui improvements Wolfgang Bumiller
  4 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-04-11 14:05 UTC (permalink / raw)
  To: pdm-devel

put the remotes panel into it's own component to keep the dashboard code
shorter when adding more functionality here.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 ui/src/dashboard/mod.rs          | 38 ++++----------
 ui/src/dashboard/remote_panel.rs | 90 ++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+), 29 deletions(-)
 create mode 100644 ui/src/dashboard/remote_panel.rs

diff --git a/ui/src/dashboard/mod.rs b/ui/src/dashboard/mod.rs
index ea0cf5e..016fccb 100644
--- a/ui/src/dashboard/mod.rs
+++ b/ui/src/dashboard/mod.rs
@@ -26,6 +26,9 @@ pub use top_entities::TopEntities;
 mod subscription_info;
 pub use subscription_info::SubscriptionInfo;
 
+mod remote_panel;
+use remote_panel::RemotePanel;
+
 #[derive(Properties, PartialEq)]
 pub struct Dashboard {
     #[prop_or(60)]
@@ -260,19 +263,7 @@ impl Component for PdmDashboard {
         }
     }
 
-    fn view(&self, _ctx: &yew::Context<Self>) -> yew::Html {
-        let (remote_icon, remote_text) = match (self.status.failed_remotes, self.status.remotes) {
-            (0, 0) => (Status::Warning.to_fa_icon(), tr!("No remotes configured.")),
-            (0, _) => (
-                Status::Success.to_fa_icon(),
-                tr!("Could reach all remotes."),
-            ),
-            (failed, _) => (
-                Status::Error.to_fa_icon(),
-                tr!("{0} remotes failed to reach.", failed),
-            ),
-        };
-
+    fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
         let content = Column::new()
             .class(FlexFit)
             .with_child(
@@ -290,22 +281,11 @@ impl Component for PdmDashboard {
                             .with_tool(
                                 Button::new(tr!("Add"))
                                     .icon_class("fa fa-plus-circle")
-                                    .onclick(_ctx.link().callback(|_| Msg::CreateWizard(true))),
+                                    .onclick(ctx.link().callback(|_| Msg::CreateWizard(true))),
                             )
-                            .with_child(
-                                Column::new()
-                                    .padding(4)
-                                    .class(FlexFit)
-                                    .class(AlignItems::Center)
-                                    .class(JustifyContent::Center)
-                                    .gap(2)
-                                    .with_child(if self.loading {
-                                        html! {<i class={"pwt-loading-icon"} />}
-                                    } else {
-                                        remote_icon.large_4x().into()
-                                    })
-                                    .with_optional_child((!self.loading).then_some(remote_text)),
-                            ),
+                            .with_child(RemotePanel::new(
+                                (!self.loading).then_some(self.status.clone()),
+                            )),
                     )
                     .with_child(self.create_node_panel(
                         "building",
@@ -423,7 +403,7 @@ impl Component for PdmDashboard {
             .with_optional_child(
                 self.show_wizard.then_some(
                     AddWizard::new(pdm_api_types::remotes::RemoteType::Pve)
-                        .on_close(_ctx.link().callback(|_| Msg::CreateWizard(false)))
+                        .on_close(ctx.link().callback(|_| Msg::CreateWizard(false)))
                         .on_submit(move |ctx| {
                             crate::remotes::create_remote(
                                 ctx,
diff --git a/ui/src/dashboard/remote_panel.rs b/ui/src/dashboard/remote_panel.rs
new file mode 100644
index 0000000..2c1dd75
--- /dev/null
+++ b/ui/src/dashboard/remote_panel.rs
@@ -0,0 +1,90 @@
+use std::rc::Rc;
+
+use proxmox_yew_comp::Status;
+use pwt::{
+    css,
+    prelude::*,
+    props::{ContainerBuilder, WidgetBuilder},
+    widget::{Column, Container, Fa},
+};
+use yew::{
+    virtual_dom::{VComp, VNode},
+    Component, Properties,
+};
+
+use pdm_api_types::resource::ResourcesStatus;
+
+#[derive(Properties, PartialEq)]
+/// A panel for showing the overall remotes status
+pub struct RemotePanel {
+    /// The status loaded from the API
+    pub status: Option<ResourcesStatus>,
+}
+
+impl RemotePanel {
+    /// Takes the status of the API, or None (which indicates loading)
+    pub fn new(status: Option<ResourcesStatus>) -> Self {
+        yew::props!(Self { status })
+    }
+}
+
+impl From<RemotePanel> for VNode {
+    fn from(val: RemotePanel) -> Self {
+        let comp = VComp::new::<PdmRemotePanel>(Rc::new(val), None);
+        VNode::from(comp)
+    }
+}
+
+struct PdmRemotePanel {}
+
+impl Component for PdmRemotePanel {
+    type Message = &'static str;
+    type Properties = RemotePanel;
+
+    fn create(_ctx: &yew::Context<Self>) -> Self {
+        Self {}
+    }
+
+    fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
+        let props = ctx.props();
+        if props.status.is_none() {
+            return Column::new()
+                .padding(4)
+                .class(css::FlexFit)
+                .class(css::AlignItems::Center)
+                .class(css::JustifyContent::Center)
+                .with_child(Fa::new("").class("pwt-loading-icon"))
+                .into();
+        }
+        let status = props.status.clone().unwrap();
+
+        let (remote_icon, remote_text, failure) = match (status.failed_remotes, status.remotes) {
+            (0, 0) => (
+                Status::Warning.to_fa_icon(),
+                tr!("No remotes configured."),
+                false,
+            ),
+            (0, _) => (
+                Status::Success.to_fa_icon(),
+                tr!("Could reach all remotes."),
+                false,
+            ),
+            (failed, _) => (
+                Status::Error.to_fa_icon(),
+                tr!("{0} remotes failed to reach.", failed),
+                true,
+            ),
+        };
+        Column::new()
+            .tabindex(if failure { 0 } else { -1 })
+            .padding(4)
+            .class(css::FlexFit)
+            .class(css::AlignItems::Center)
+            .class(css::JustifyContent::Center)
+            .style("cursor", failure.then_some("pointer"))
+            .gap(2)
+            .with_child(remote_icon.large_4x())
+            .with_child(Container::new().with_child(remote_text))
+            .into()
+    }
+}
-- 
2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pdm-devel] [PATCH datacenter-manager 3/3] ui: dashboard: refactor guest panel
  2025-04-11 14:05 [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements Dominik Csapak
                   ` (2 preceding siblings ...)
  2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 2/3] ui: dashboard: refactor remotes panel Dominik Csapak
@ 2025-04-11 14:05 ` Dominik Csapak
  2025-04-16  9:01 ` [pdm-devel] applied-series: [PATCH yew-comp/datacenter-manager] some ui improvements Wolfgang Bumiller
  4 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2025-04-11 14:05 UTC (permalink / raw)
  To: pdm-devel

so we can keep the dashboard code smaller when adding new features here.
Also implement showing unknown guests and templates

To have a more structured output, re-implement it with a DataTable
instead of a custom flex layout. This has the advantage of being
a proper grid.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 ui/src/dashboard/guest_panel.rs | 143 ++++++++++++++++++++++++++++++++
 ui/src/dashboard/mod.rs         |  65 ++++-----------
 ui/src/pve/mod.rs               |  11 ++-
 3 files changed, 169 insertions(+), 50 deletions(-)
 create mode 100644 ui/src/dashboard/guest_panel.rs

diff --git a/ui/src/dashboard/guest_panel.rs b/ui/src/dashboard/guest_panel.rs
new file mode 100644
index 0000000..d885056
--- /dev/null
+++ b/ui/src/dashboard/guest_panel.rs
@@ -0,0 +1,143 @@
+use std::rc::Rc;
+
+use pdm_api_types::resource::GuestStatusCount;
+use proxmox_yew_comp::GuestState;
+use pwt::{
+    prelude::*,
+    props::ExtractPrimaryKey,
+    state::Store,
+    widget::{
+        data_table::{DataTable, DataTableColumn, DataTableHeader},
+        Fa,
+    },
+};
+use yew::virtual_dom::{VComp, VNode};
+
+use crate::pve::GuestType;
+
+use super::loading_column;
+
+#[derive(PartialEq, Clone, Properties)]
+pub struct GuestPanel {
+    guest_type: GuestType,
+    status: Option<GuestStatusCount>,
+}
+
+impl GuestPanel {
+    pub fn new(guest_type: GuestType, status: Option<GuestStatusCount>) -> Self {
+        yew::props!(Self { guest_type, status })
+    }
+}
+
+impl From<GuestPanel> for VNode {
+    fn from(value: GuestPanel) -> Self {
+        let comp = VComp::new::<PdmGuestPanel>(Rc::new(value), None);
+        VNode::from(comp)
+    }
+}
+
+#[derive(PartialEq, Clone)]
+pub enum StatusRow {
+    State(GuestState, u64),
+    All(u64),
+}
+
+impl ExtractPrimaryKey for StatusRow {
+    fn extract_key(&self) -> yew::virtual_dom::Key {
+        yew::virtual_dom::Key::from(match self {
+            StatusRow::State(state, _) => match state {
+                GuestState::Running => "running",
+                GuestState::Paused => "paused",
+                GuestState::Stopped => "stopped",
+                GuestState::Template => "template",
+                GuestState::Unknown => "unknown",
+            },
+            StatusRow::All(_) => "all",
+        })
+    }
+}
+
+fn columns(guest_type: GuestType) -> Rc<Vec<DataTableHeader<StatusRow>>> {
+    Rc::new(vec![
+        DataTableColumn::new("icon")
+            .width("3em")
+            .render(move |item: &StatusRow| {
+                match item {
+                    StatusRow::State(state, _) => state.to_fa_icon(),
+                    StatusRow::All(_) => match guest_type {
+                        GuestType::Qemu => Fa::new("desktop"),
+                        GuestType::Lxc => Fa::new("cubes"),
+                    },
+                }
+                .fixed_width()
+                .into()
+            })
+            .into(),
+        DataTableColumn::new("text")
+            .flex(5)
+            .render(|item: &StatusRow| {
+                match item {
+                    StatusRow::State(GuestState::Running, _) => tr!("running"),
+                    StatusRow::State(GuestState::Stopped, _) => tr!("stopped"),
+                    StatusRow::State(GuestState::Paused, _) => tr!("paused"),
+                    StatusRow::State(GuestState::Template, _) => tr!("Template"),
+                    StatusRow::State(GuestState::Unknown, _) => tr!("Unknown"),
+                    StatusRow::All(_) => tr!("All"),
+                }
+                .into()
+            })
+            .into(),
+        DataTableColumn::new("count")
+            .flex(1)
+            .justify("right")
+            .render(|item: &StatusRow| match item {
+                StatusRow::State(_, count) => count.into(),
+                StatusRow::All(count) => count.into(),
+            })
+            .into(),
+    ])
+}
+
+pub struct PdmGuestPanel {}
+
+impl yew::Component for PdmGuestPanel {
+    type Message = String;
+    type Properties = GuestPanel;
+
+    fn create(_ctx: &yew::Context<Self>) -> Self {
+        Self {}
+    }
+
+    fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
+        let props = ctx.props();
+        if props.status.is_none() {
+            return loading_column().into();
+        }
+        let guest_type = props.guest_type;
+        let status = ctx.props().status.clone().unwrap();
+
+        let store = Store::new();
+        store.set_data(vec![
+            StatusRow::State(GuestState::Running, status.running),
+            StatusRow::State(GuestState::Stopped, status.stopped),
+            StatusRow::State(GuestState::Template, status.template),
+            StatusRow::State(GuestState::Unknown, status.unknown),
+            StatusRow::All(status.running + status.stopped + status.template + status.unknown),
+        ]);
+
+        store.set_filter(|rec: &StatusRow| match rec {
+            StatusRow::State(_, count) if *count > 0 => true,
+            StatusRow::State(GuestState::Running | GuestState::Stopped, _) => true,
+            StatusRow::All(_) => true,
+            _ => false,
+        });
+
+        DataTable::new(columns(guest_type), store.clone())
+            .padding(4)
+            .striped(false)
+            .borderless(true)
+            .bordered(false)
+            .show_header(false)
+            .into()
+    }
+}
diff --git a/ui/src/dashboard/mod.rs b/ui/src/dashboard/mod.rs
index 016fccb..7b7ec81 100644
--- a/ui/src/dashboard/mod.rs
+++ b/ui/src/dashboard/mod.rs
@@ -7,7 +7,7 @@ use yew::{
     Component,
 };
 
-use proxmox_yew_comp::{http_get, GuestState, Status};
+use proxmox_yew_comp::{http_get, Status};
 use pwt::{
     css::{AlignItems, FlexFit, FlexWrap, JustifyContent},
     prelude::*,
@@ -18,7 +18,7 @@ use pwt::{
 use pdm_api_types::resource::{GuestStatusCount, NodeStatusCount, ResourcesStatus};
 use pdm_client::types::TopEntity;
 
-use crate::{remotes::AddWizard, RemoteList};
+use crate::{pve::GuestType, remotes::AddWizard, RemoteList};
 
 mod top_entities;
 pub use top_entities::TopEntities;
@@ -29,6 +29,9 @@ pub use subscription_info::SubscriptionInfo;
 mod remote_panel;
 use remote_panel::RemotePanel;
 
+mod guest_panel;
+use guest_panel::GuestPanel;
+
 #[derive(Properties, PartialEq)]
 pub struct Dashboard {
     #[prop_or(60)]
@@ -120,48 +123,20 @@ impl PdmDashboard {
             )
     }
 
-    fn create_guest_panel(&self, icon: &str, title: String, status: &GuestStatusCount) -> Panel {
+    fn create_guest_panel(&self, guest_type: GuestType, status: &GuestStatusCount) -> Panel {
+        let (icon, title) = match guest_type {
+            GuestType::Qemu => ("desktop", tr!("Virtual Machines")),
+            GuestType::Lxc => ("cubses", tr!("Linux Container")),
+        };
         Panel::new()
             .flex(1.0)
             .width(300)
             .title(self.create_title_with_icon(icon, title))
             .border(true)
-            .with_child(if self.loading {
-                loading_column()
-            } else {
-                Column::new()
-                    .padding(4)
-                    .gap(2)
-                    .class(FlexFit)
-                    .class(JustifyContent::Center)
-                    .with_child(
-                        Row::new()
-                            .gap(2)
-                            .with_child(GuestState::Running.to_fa_icon().fixed_width())
-                            .with_child(tr!("running"))
-                            .with_flex_spacer()
-                            .with_child(Container::from_tag("span").with_child(status.running)),
-                    )
-                    .with_child(
-                        Row::new()
-                            .gap(2)
-                            .with_child(GuestState::Stopped.to_fa_icon().fixed_width())
-                            .with_child(tr!("stopped"))
-                            .with_flex_spacer()
-                            .with_child(Container::from_tag("span").with_child(status.stopped)),
-                    )
-                    // FIXME: show templates?
-                    .with_optional_child(
-                        (self.status.qemu.unknown > 0).then_some(
-                            Row::new()
-                                .gap(2)
-                                .with_child(GuestState::Unknown.to_fa_icon().fixed_width())
-                                .with_child(tr!("unknown"))
-                                .with_flex_spacer()
-                                .with_child(Container::from_tag("span").with_child(status.unknown)),
-                        ),
-                    )
-            })
+            .with_child(GuestPanel::new(
+                guest_type,
+                (!self.loading).then_some(status.clone()),
+            ))
     }
 
     fn create_top_entities_panel(
@@ -292,16 +267,8 @@ impl Component for PdmDashboard {
                         tr!("Virtual Environment Nodes"),
                         &self.status.pve_nodes,
                     ))
-                    .with_child(self.create_guest_panel(
-                        "desktop",
-                        tr!("Virtual Machines"),
-                        &self.status.qemu,
-                    ))
-                    .with_child(self.create_guest_panel(
-                        "cubes",
-                        tr!("Linux Container"),
-                        &self.status.lxc,
-                    ))
+                    .with_child(self.create_guest_panel(GuestType::Qemu, &self.status.qemu))
+                    .with_child(self.create_guest_panel(GuestType::Lxc, &self.status.lxc))
                     // FIXME: add PBS support
                     //.with_child(self.create_node_panel(
                     //    "building-o",
diff --git a/ui/src/pve/mod.rs b/ui/src/pve/mod.rs
index ed0ee04..c5a83cf 100644
--- a/ui/src/pve/mod.rs
+++ b/ui/src/pve/mod.rs
@@ -1,4 +1,4 @@
-use std::rc::Rc;
+use std::{fmt::Display, rc::Rc};
 
 use gloo_utils::window;
 use proxmox_client::Error;
@@ -72,6 +72,15 @@ pub enum GuestType {
     Lxc,
 }
 
+impl Display for GuestType {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            GuestType::Qemu => f.write_str("qemu"),
+            GuestType::Lxc => f.write_str("lxc"),
+        }
+    }
+}
+
 #[derive(PartialEq, Clone, Copy)]
 pub struct GuestInfo {
     pub guest_type: GuestType,
-- 
2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status
  2025-04-11 14:05 ` [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status Dominik Csapak
@ 2025-04-14 14:50   ` Wolfgang Bumiller
  2025-04-15 11:52     ` Dominik Csapak
  0 siblings, 1 reply; 9+ messages in thread
From: Wolfgang Bumiller @ 2025-04-14 14:50 UTC (permalink / raw)
  To: Dominik Csapak; +Cc: pdm-devel

On Fri, Apr 11, 2025 at 04:05:17PM +0200, Dominik Csapak wrote:
> this is sensible to have, e.g. when we want to have the status structs
> as parto of a type for a store

I'd argue these should all be:

    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]

> 
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
>  src/status.rs | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/src/status.rs b/src/status.rs
> index 9d951c5..ab77a15 100644
> --- a/src/status.rs
> +++ b/src/status.rs
> @@ -7,6 +7,7 @@ use pwt::{
>  
>  /// Used to represent a Status of some resource or component, e.g.
>  /// if a PVE node is online or not.
> +#[derive(PartialEq, Clone)]
>  pub enum Status {
>      Success,
>      Warning,
> @@ -27,6 +28,7 @@ impl Status {
>  }
>  
>  /// Used to represent the state of a Node, being PVE or PBS
> +#[derive(PartialEq, Clone)]
>  pub enum NodeState {
>      Online,
>      Offline,
> @@ -45,6 +47,7 @@ impl NodeState {
>  }
>  
>  /// Used to represent the state of a PVE guest, such as a VM
> +#[derive(PartialEq, Clone)]
>  pub enum GuestState {
>      Running,
>      Paused,
> @@ -67,6 +70,7 @@ impl GuestState {
>  }
>  
>  /// Used to represent the state of a Storage or Datastore
> +#[derive(PartialEq, Clone)]
>  pub enum StorageState {
>      Available,
>      Unavailable,
> -- 
> 2.39.5


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status
  2025-04-14 14:50   ` Wolfgang Bumiller
@ 2025-04-15 11:52     ` Dominik Csapak
  2025-04-15 12:57       ` Wolfgang Bumiller
  0 siblings, 1 reply; 9+ messages in thread
From: Dominik Csapak @ 2025-04-15 11:52 UTC (permalink / raw)
  To: Wolfgang Bumiller; +Cc: pdm-devel

On 4/14/25 16:50, Wolfgang Bumiller wrote:
> On Fri, Apr 11, 2025 at 04:05:17PM +0200, Dominik Csapak wrote:
>> this is sensible to have, e.g. when we want to have the status structs
>> as parto of a type for a store
> 
> I'd argue these should all be:
> 
>      #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
> 

sure, makes sense.

is a follow up enough or should I resend the patch ( or whole series?)

>>
>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>> ---
>>   src/status.rs | 4 ++++
>>   1 file changed, 4 insertions(+)
>>
>> diff --git a/src/status.rs b/src/status.rs
>> index 9d951c5..ab77a15 100644
>> --- a/src/status.rs
>> +++ b/src/status.rs
>> @@ -7,6 +7,7 @@ use pwt::{
>>   
>>   /// Used to represent a Status of some resource or component, e.g.
>>   /// if a PVE node is online or not.
>> +#[derive(PartialEq, Clone)]
>>   pub enum Status {
>>       Success,
>>       Warning,
>> @@ -27,6 +28,7 @@ impl Status {
>>   }
>>   
>>   /// Used to represent the state of a Node, being PVE or PBS
>> +#[derive(PartialEq, Clone)]
>>   pub enum NodeState {
>>       Online,
>>       Offline,
>> @@ -45,6 +47,7 @@ impl NodeState {
>>   }
>>   
>>   /// Used to represent the state of a PVE guest, such as a VM
>> +#[derive(PartialEq, Clone)]
>>   pub enum GuestState {
>>       Running,
>>       Paused,
>> @@ -67,6 +70,7 @@ impl GuestState {
>>   }
>>   
>>   /// Used to represent the state of a Storage or Datastore
>> +#[derive(PartialEq, Clone)]
>>   pub enum StorageState {
>>       Available,
>>       Unavailable,
>> -- 
>> 2.39.5



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status
  2025-04-15 11:52     ` Dominik Csapak
@ 2025-04-15 12:57       ` Wolfgang Bumiller
  0 siblings, 0 replies; 9+ messages in thread
From: Wolfgang Bumiller @ 2025-04-15 12:57 UTC (permalink / raw)
  To: Dominik Csapak; +Cc: pdm-devel

On Tue, Apr 15, 2025 at 01:52:22PM +0200, Dominik Csapak wrote:
> On 4/14/25 16:50, Wolfgang Bumiller wrote:
> > On Fri, Apr 11, 2025 at 04:05:17PM +0200, Dominik Csapak wrote:
> > > this is sensible to have, e.g. when we want to have the status structs
> > > as parto of a type for a store
> > 
> > I'd argue these should all be:
> > 
> >      #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
> > 
> 
> sure, makes sense.
> 
> is a follow up enough or should I resend the patch ( or whole series?)

Either way is fine. I'd hold off on the pdm patches until after the bump
of yew-comp, to avoid it being in a non-buildable state too long.

We could derive more standard traits in a few more places probably, eg:
src/status.rs: Status, NodeState, GuestState, StorageState
src/common_api_types.rs: TaskStatusClass (misses Debug+Eq+Hash)
src/proxmox_product.rs: ExistingProduct (only misses Eq+Hash)
src/journal_view.rs: Position (only misses Eq+Hash)

> 
> > > 
> > > Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> > > ---
> > >   src/status.rs | 4 ++++
> > >   1 file changed, 4 insertions(+)
> > > 
> > > diff --git a/src/status.rs b/src/status.rs
> > > index 9d951c5..ab77a15 100644
> > > --- a/src/status.rs
> > > +++ b/src/status.rs
> > > @@ -7,6 +7,7 @@ use pwt::{
> > >   /// Used to represent a Status of some resource or component, e.g.
> > >   /// if a PVE node is online or not.
> > > +#[derive(PartialEq, Clone)]
> > >   pub enum Status {
> > >       Success,
> > >       Warning,
> > > @@ -27,6 +28,7 @@ impl Status {
> > >   }
> > >   /// Used to represent the state of a Node, being PVE or PBS
> > > +#[derive(PartialEq, Clone)]
> > >   pub enum NodeState {
> > >       Online,
> > >       Offline,
> > > @@ -45,6 +47,7 @@ impl NodeState {
> > >   }
> > >   /// Used to represent the state of a PVE guest, such as a VM
> > > +#[derive(PartialEq, Clone)]
> > >   pub enum GuestState {
> > >       Running,
> > >       Paused,
> > > @@ -67,6 +70,7 @@ impl GuestState {
> > >   }
> > >   /// Used to represent the state of a Storage or Datastore
> > > +#[derive(PartialEq, Clone)]
> > >   pub enum StorageState {
> > >       Available,
> > >       Unavailable,
> > > -- 
> > > 2.39.5
> 
> 


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pdm-devel] applied-series: [PATCH yew-comp/datacenter-manager] some ui improvements
  2025-04-11 14:05 [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements Dominik Csapak
                   ` (3 preceding siblings ...)
  2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 3/3] ui: dashboard: refactor guest panel Dominik Csapak
@ 2025-04-16  9:01 ` Wolfgang Bumiller
  4 siblings, 0 replies; 9+ messages in thread
From: Wolfgang Bumiller @ 2025-04-16  9:01 UTC (permalink / raw)
  To: Dominik Csapak; +Cc: pdm-devel

applied series, thanks

On Fri, Apr 11, 2025 at 04:05:16PM +0200, Dominik Csapak wrote:
> Some smaller improvements for the ui, mostly refactor and preparation
> for an upcoming series that improves the search box.
> 
> pdm patch 3/3 depends on the proxmox-yew-comp patch.
> 
> proxmox-yew-comp:
> 
> Dominik Csapak (1):
>   status: implement PartialEq and Clone for Status
> 
>  src/status.rs | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> proxmox-datacenter-manager:
> 
> Dominik Csapak (3):
>   ui: improve keyboard navigation for search box
>   ui: dashboard: refactor remotes panel
>   ui: dashboard: refactor guest panel
> 
>  ui/src/dashboard/guest_panel.rs  | 143 +++++++++++++++++++++++++++++++
>  ui/src/dashboard/mod.rs          | 103 ++++++----------------
>  ui/src/dashboard/remote_panel.rs |  90 +++++++++++++++++++
>  ui/src/pve/mod.rs                |  11 ++-
>  ui/src/widget/resource_tree.rs   |  76 ++++++++++------
>  5 files changed, 318 insertions(+), 105 deletions(-)
>  create mode 100644 ui/src/dashboard/guest_panel.rs
>  create mode 100644 ui/src/dashboard/remote_panel.rs
> 
> -- 
> 2.39.5


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-04-16  9:02 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-04-11 14:05 [pdm-devel] [PATCH yew-comp/datacenter-manager] some ui improvements Dominik Csapak
2025-04-11 14:05 ` [pdm-devel] [PATCH yew-comp 1/1] status: implement PartialEq and Clone for Status Dominik Csapak
2025-04-14 14:50   ` Wolfgang Bumiller
2025-04-15 11:52     ` Dominik Csapak
2025-04-15 12:57       ` Wolfgang Bumiller
2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 1/3] ui: improve keyboard navigation for search box Dominik Csapak
2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 2/3] ui: dashboard: refactor remotes panel Dominik Csapak
2025-04-11 14:05 ` [pdm-devel] [PATCH datacenter-manager 3/3] ui: dashboard: refactor guest panel Dominik Csapak
2025-04-16  9:01 ` [pdm-devel] applied-series: [PATCH yew-comp/datacenter-manager] some ui improvements Wolfgang Bumiller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal