all lists on 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal