From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pdm-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id CEF531FF164 for <inbox@lore.proxmox.com>; Fri, 11 Apr 2025 16:05:29 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id EE9151C064; Fri, 11 Apr 2025 16:05:24 +0200 (CEST) From: Dominik Csapak <d.csapak@proxmox.com> To: pdm-devel@lists.proxmox.com Date: Fri, 11 Apr 2025 16:05:20 +0200 Message-Id: <20250411140520.1475644-5-d.csapak@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250411140520.1475644-1-d.csapak@proxmox.com> References: <20250411140520.1475644-1-d.csapak@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.022 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 3/3] ui: dashboard: refactor guest panel X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion <pdm-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pdm-devel>, <mailto:pdm-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pdm-devel/> List-Post: <mailto:pdm-devel@lists.proxmox.com> List-Help: <mailto:pdm-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel>, <mailto:pdm-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox Datacenter Manager development discussion <pdm-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" <pdm-devel-bounces@lists.proxmox.com> 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