From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 550A21FF191 for ; Tue, 21 Oct 2025 16:08:11 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id ED7CC9A5; Tue, 21 Oct 2025 16:08:36 +0200 (CEST) From: Dominik Csapak To: pdm-devel@lists.proxmox.com Date: Tue, 21 Oct 2025 16:03:17 +0200 Message-ID: <20251021140801.3611022-2-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251021140801.3611022-1-d.csapak@proxmox.com> References: <20251021140801.3611022-1-d.csapak@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.028 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pdm-devel] [PATCH datacenter-manager 01/15] ui: dashboard: refactor guest panel creation to its own module X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Datacenter Manager development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" so we can more easily reuse it. For this, also make the 'create_title_with_icon' a freestanding function that is public so we can reuse it outside the dashboard struct. Signed-off-by: Dominik Csapak --- ui/src/dashboard/guest_panel.rs | 75 +++++++++++++++++++++++++-------- ui/src/dashboard/mod.rs | 66 ++++++++++++----------------- 2 files changed, 84 insertions(+), 57 deletions(-) diff --git a/ui/src/dashboard/guest_panel.rs b/ui/src/dashboard/guest_panel.rs index 814ecfa5..3197feb4 100644 --- a/ui/src/dashboard/guest_panel.rs +++ b/ui/src/dashboard/guest_panel.rs @@ -1,30 +1,32 @@ use std::rc::Rc; -use pdm_api_types::resource::{GuestStatusCount, ResourceType}; +use pdm_api_types::resource::{GuestStatusCount, ResourceType, ResourcesStatus}; use pdm_search::{Search, SearchTerm}; use proxmox_yew_comp::GuestState; use pwt::{ css::{self, TextAlign}, prelude::*, - widget::{Container, Fa, List, ListTile}, + widget::{Container, Fa, List, ListTile, Panel}, }; use yew::{ virtual_dom::{VComp, VNode}, Properties, }; -use crate::{pve::GuestType, search_provider::get_search_provider}; +use crate::{ + dashboard::create_title_with_icon, pve::GuestType, search_provider::get_search_provider, +}; use super::loading_column; #[derive(PartialEq, Clone, Properties)] pub struct GuestPanel { - guest_type: GuestType, - status: Option, + guest_type: Option, + status: Option, } impl GuestPanel { - pub fn new(guest_type: GuestType, status: Option) -> Self { + pub fn new(guest_type: Option, status: Option) -> Self { yew::props!(Self { guest_type, status }) } } @@ -63,7 +65,16 @@ impl yew::Component for PdmGuestPanel { let props = ctx.props(); let guest_type = props.guest_type; let status = match &props.status { - Some(status) => status, + Some(status) => match guest_type { + Some(GuestType::Qemu) => status.qemu.clone(), + Some(GuestType::Lxc) => status.lxc.clone(), + None => GuestStatusCount { + running: status.qemu.running + status.lxc.running, + stopped: status.qemu.stopped + status.lxc.stopped, + template: status.qemu.template + status.lxc.template, + unknown: status.qemu.unknown + status.lxc.unknown, + }, + }, None => return loading_column().into(), }; @@ -93,7 +104,7 @@ impl yew::Component for PdmGuestPanel { fn create_list_tile( link: &html::Scope, - guest_type: GuestType, + guest_type: Option, status_row: StatusRow, ) -> Option { let (icon, text, count, status, template) = match status_row { @@ -129,7 +140,13 @@ fn create_list_tile( None, ), }, - StatusRow::All(count) => (Fa::from(guest_type), tr!("All"), count, None, None), + StatusRow::All(count) => ( + Fa::from(guest_type.unwrap_or(GuestType::Qemu)), + tr!("All"), + count, + None, + None, + ), }; Some( @@ -158,19 +175,29 @@ fn create_list_tile( } fn create_guest_search_term( - guest_type: GuestType, + guest_type: Option, status: Option<&'static str>, template: Option, ) -> Search { - let resource_type: ResourceType = guest_type.into(); - if status.is_none() && template.is_none() { - return Search::with_terms(vec![ - SearchTerm::new(resource_type.as_str()).category(Some("type")) - ]); + let mut terms = Vec::new(); + match guest_type { + Some(guest_type) => { + let resource_type: ResourceType = guest_type.into(); + terms.push(SearchTerm::new(resource_type.as_str()).category(Some("type"))); + } + None => { + terms.push( + SearchTerm::new(ResourceType::PveQemu.as_str()) + .category(Some("type")) + .optional(true), + ); + terms.push( + SearchTerm::new(ResourceType::PveLxc.as_str()) + .category(Some("type")) + .optional(true), + ); + } } - - let mut terms = vec![SearchTerm::new(resource_type.as_str()).category(Some("type"))]; - if let Some(template) = template { terms.push(SearchTerm::new(template.to_string()).category(Some("template"))); } @@ -179,3 +206,15 @@ fn create_guest_search_term( } Search::with_terms(terms) } + +pub fn create_guest_panel(guest_type: Option, status: Option) -> Panel { + let (icon, title) = match guest_type { + Some(GuestType::Qemu) => ("desktop", tr!("Virtual Machines")), + Some(GuestType::Lxc) => ("cubes", tr!("Linux Container")), + None => ("desktop", tr!("Guests")), + }; + Panel::new() + .title(create_title_with_icon(icon, title)) + .border(true) + .with_child(GuestPanel::new(guest_type, status)) +} diff --git a/ui/src/dashboard/mod.rs b/ui/src/dashboard/mod.rs index f54c509c..602c8a3b 100644 --- a/ui/src/dashboard/mod.rs +++ b/ui/src/dashboard/mod.rs @@ -46,7 +46,7 @@ mod remote_panel; use remote_panel::RemotePanel; mod guest_panel; -use guest_panel::GuestPanel; +pub use guest_panel::create_guest_panel; mod sdn_zone_panel; use sdn_zone_panel::SdnZonePanel; @@ -149,15 +149,6 @@ pub struct PdmDashboard { } impl PdmDashboard { - fn create_title_with_icon(&self, icon: &str, title: String) -> Html { - Row::new() - .class(AlignItems::Center) - .gap(2) - .with_child(Fa::new(icon)) - .with_child(title) - .into() - } - fn create_node_panel(&self, ctx: &yew::Context, icon: &str, title: String) -> Panel { let mut search_terms = vec![SearchTerm::new("node").category(Some("type"))]; let (status_icon, text): (Fa, String) = match &self.status { @@ -203,7 +194,7 @@ impl PdmDashboard { Panel::new() .flex(1.0) .width(300) - .title(self.create_title_with_icon(icon, title)) + .title(create_title_with_icon(icon, title)) .border(true) .with_child( Column::new() @@ -233,34 +224,13 @@ impl PdmDashboard { ) } - fn create_guest_panel(&self, guest_type: GuestType) -> Panel { - let (icon, title, status) = match guest_type { - GuestType::Qemu => ( - "desktop", - tr!("Virtual Machines"), - self.status.as_ref().map(|s| s.qemu.clone()), - ), - GuestType::Lxc => ( - "cubes", - tr!("Linux Container"), - self.status.as_ref().map(|s| s.lxc.clone()), - ), - }; - Panel::new() - .flex(1.0) - .width(300) - .title(self.create_title_with_icon(icon, title)) - .border(true) - .with_child(GuestPanel::new(guest_type, status)) - } - fn create_sdn_panel(&self) -> Panel { let sdn_zones_status = self.status.as_ref().map(|status| status.sdn_zones.clone()); Panel::new() .flex(1.0) .width(200) - .title(self.create_title_with_icon("sdn", tr!("SDN Zones"))) + .title(create_title_with_icon("sdn", tr!("SDN Zones"))) .border(true) .with_child(SdnZonePanel::new( (!self.loading).then_some(sdn_zones_status).flatten(), @@ -281,7 +251,7 @@ impl PdmDashboard { .flex(1.0) .width(500) .border(true) - .title(self.create_title_with_icon("list", title)) + .title(create_title_with_icon("list", title)) .with_child( Container::new() .class(FlexFit) @@ -318,7 +288,7 @@ impl PdmDashboard { .width(500) .min_width(400) .border(true) - .title(self.create_title_with_icon(icon, title)) + .title(create_title_with_icon(icon, title)) .with_optional_child( entities .map(|entities| TopEntities::new(entities.clone(), metrics_title, threshold)), @@ -537,7 +507,7 @@ impl Component for PdmDashboard { .padding_top(0) .with_child( Panel::new() - .title(self.create_title_with_icon("server", tr!("Remotes"))) + .title(create_title_with_icon("server", tr!("Remotes"))) .flex(1.0) //.border(true) .width(300) @@ -568,8 +538,16 @@ impl Component for PdmDashboard { "building", tr!("Virtual Environment Nodes"), )) - .with_child(self.create_guest_panel(GuestType::Qemu)) - .with_child(self.create_guest_panel(GuestType::Lxc)) + .with_child( + create_guest_panel(Some(GuestType::Qemu), self.status.clone()) + .flex(1.0) + .width(300), + ) + .with_child( + create_guest_panel(Some(GuestType::Lxc), self.status.clone()) + .flex(1.0) + .width(300), + ) // FIXME: add PBS support //.with_child(self.create_node_panel( // "building-o", @@ -580,7 +558,7 @@ impl Component for PdmDashboard { // Panel::new() // .flex(1.0) // .width(300) - // .title(self.create_title_with_icon( + // .title(create_title_with_icon( // "floppy-o", // tr!("Backup Server Datastores"), // )) @@ -777,3 +755,13 @@ fn loading_column() -> Column { .class(AlignItems::Center) .with_child(html! {}) } + +/// Create a consistent title component for the given title and icon +pub fn create_title_with_icon(icon: &str, title: String) -> Html { + Row::new() + .class(AlignItems::Center) + .gap(2) + .with_child(Fa::new(icon)) + .with_child(title) + .into() +} -- 2.47.3 _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel